JZOJ5918【NOIP2018模拟10.20】Car
题目
最近比较懒,题目描述都直接截图了。
题目大意
给你一棵树,还有树上的几条路径,一条路径上的点到路径上其它任意点的代价为111。然后是一堆询问,问从一个点到另一个点的最小代价。
思路
一开始做这题时,就自然地往链剖或倍增方面想。
链剖想不出,于是就想倍增。
对于每个点,预处理出它花111的代价能到达的最远祖先。
然后倍增一下~
询问的时候,就将两个点的LCALCALCA求出,然后两个点往上跳,直到再跳就超过LCALCALCA为止。
所以问题就转化成了求两个点是否被一条路径直接连通,如果是,就加111,否则加222。
想不出来……
最后只能打暴力:将一个点作为根,然后粗暴地处理……
正解
事实证明我前面所思考的是正确的。
对于这个问题,其实可以转化成:是否有一条路径的两个端点分别在这两个点的子树内……
好像是很显然的,可我为什么想不到……
于是我们就可以求出它们的dfndfndfn序,这就成了一个平面上的问题。
每一条路径可以看做一个点,每一个询问可以看作一个矩形,求这个矩形当中是否有点出现。
然后就是一个简单的扫描线和树状数组了。
代码
有一点需要补充一下:
有的同学反映,这题递归会爆栈,所以要打人工栈。
反正我是没有爆栈……
后来听说xzb大爷想出一个不用dfs求dfn序的方法。
首先,我们可以通过拓扑排序之类的自底向上递推求出它们的siz等信息。(这题比较良心,由于每个点的父亲编号小于它,所以直接从后往前扫就好了。)
然后自顶向下(可以用bfs,这题直接从前往后扫就好了),
对于每个点分两种情况:
- 它是父亲的第一个儿子。那么它的dfndfndfn即为父亲加一。
- 否则,就是父亲上一个儿子的dfndfndfn加它的sizsizsiz。
然后就可以简单地求出dfndfndfn了。
正常的bfs中,上一个点就是父亲的上一个儿子。
不过这题不用bfs,直接记录就好了。
在此膜拜xzb
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 200000
#define M N
#define Q N
int n;
int last[N+1];
int siz[N+1],dfn[N+1];
int fa[N+1][19],dep[N+1];
inline void get_dfn(){//这是一种不需要dfs求dfn的方法,解释见上
dfn[1]=1;
for (int i=2;i<=n;++i){
if (last[fa[i][0]])
dfn[i]=dfn[last[fa[i][0]]]+siz[last[fa[i][0]]];
else
dfn[i]=dfn[fa[i][0]]+1;
last[fa[i][0]]=i;
}
}
inline int LCA(int u,int v){
if (dep[u]<dep[v])
swap(u,v);
for (int k=dep[u]-dep[v],i=0;k;k>>=1,++i)
if (k&1)
u=fa[u][i];
if (u==v)
return u;
for (int i=17;i>=0;--i)
if (fa[u][i]!=fa[v][i]){
u=fa[u][i];
v=fa[v][i];
}
return fa[u][0];
}
int m;
int up[N+1][19],dep2[N+1];
struct Oper{
int x,l,r;
int ty;
int num;
} o[M*2+Q*2+1];
int cnt;
bool cmp(const Oper &a,const Oper &b){
return a.x<b.x || a.x==b.x && a.ty<b.ty;
}
int t[N+1];
#define lowbit(x) ((x)&(-x))
inline void change(int x){
do{
t[x]++;
x+=lowbit(x);
}
while (x<=n);
}
inline int query(int x){
int res=0;
while (x){
res+=t[x];
x-=lowbit(x);
}
return res;
}
int ans[Q+1],cha[Q+1];
int main(){
freopen("car.in","r",stdin);
freopen("car.out","w",stdout);
scanf("%d",&n);
for (int i=2;i<=n;++i){
scanf("%d",&fa[i][0]);
e[++ne]={i,last[fa[i][0]]};
last[fa[i][0]]=e+ne;
}
for (int i=1;i<=n;++i)
dep[i]=dep[fa[i][0]]+1;//因为题目说每个点父亲的编号一定小于它,所以直接递推就好,下同。
for (int i=n;i>=1;--i)
siz[fa[i][0]]+=++siz[i];
get_dfn(1);
for (int i=1;i<=17;++i)
for (int j=1;j<=n;++j)
fa[j][i]=fa[fa[j][i-1]][i-1];//倍增处理
for (int i=1;i<=n;++i)
up[i][0]=i;//up[i][j]表示花费2^j的代价最远能到达的祖先
scanf("%d",&m);
for (int i=1;i<=m;++i){
int u,v;
scanf("%d%d",&u,&v);
if (dfn[u]>dfn[v])
swap(u,v);
//处理花费1代价能到达的最远祖先
int lca=LCA(u,v);
if (dep[lca]<dep[up[u][0]])
up[u][0]=lca;
if (dep[lca]<dep[up[v][0]])
up[v][0]=lca;
//加入操作列表中,相当于点(因为它有对称性,所以正着反着都加进去)
o[++cnt]={dfn[u],dfn[v],dfn[v],0,0};
o[++cnt]={dfn[v],dfn[u],dfn[u],0,0};
}
for (int i=n;i>=1;--i)
if (dep[up[i][0]]<dep[up[fa[i][0]][0]])
up[fa[i][0]][0]=up[i][0];//从底向上递推出每个点花费1代价到达的最远祖先
for (int i=1;i<=18;++i)
for (int j=1;j<=n;++j)
up[j][i]=up[up[j][i-1]][i-1];
int q;
scanf("%d",&q);
for (int i=1;i<=q;++i){
int u,v;
scanf("%d%d",&u,&v);
if (u==v)
continue;
if (up[u][18]!=up[v][18]){//判断两个点是否可以互相到达(因为N=200000,2^18>N,所以这是它能到达的最远祖先,因为这是一棵树,所以如果可达到的最远祖先不一样,那么他们就不能互相到达)
ans[i]=-1;
continue;
}
int lca=LCA(u,v);
if (lca==u || lca==v){//在同一条链上的情况
if (lca==u)
swap(u,v);
for (int j=17;j>=0;--j)
if (dep[up[u][j]]>dep[v])
u=up[u][j],ans[i]+=1<<j;//跳到LCA下的最远点
ans[i]++;
continue;
}
for (int j=17;j>=0;--j)
if (dep[up[u][j]]>dep[lca])
u=up[u][j],ans[i]+=1<<j;//跳到LCA下的最远点,下同
for (int j=17;j>=0;--j)
if (dep[up[v][j]]>dep[lca])
v=up[v][j],ans[i]+=1<<j;
//加入操作
o[++cnt]={dfn[u]-1,dfn[v],dfn[v]+siz[v]-1,1,i};
o[++cnt]={dfn[u]+siz[u],dfn[v],dfn[v]+siz[v]-1,-1,i};
}
sort(o+1,o+cnt+1,cmp);
for (int i=1;i<=cnt;++i)//扫描线求出二维数点问题
if (o[i].ty==0)
change(o[i].l);
else if (o[i].ty==1)
cha[o[i].num]=query(o[i].r)-query(o[i].l-1);
else
ans[o[i].num]+=((query(o[i].r)-query(o[i].l-1))-cha[o[i].num]?1:2);
for (int i=1;i<=q;++i)
printf("%d\n",ans[i]);
return 0;
}
总结
以后,看见有关树的题目,如果普通的倍增、链剖都做不出来,就得要往dfndfndfn序方面想……
JZOJ5918【NOIP2018模拟10.20】Car的更多相关文章
- [JZOJ NOIP2018模拟10.20 B组]
T1:原根(math) 题目链接: http://172.16.0.132/senior/#contest/show/2532/0 题目: 题解: 一个数m原根的个数是$\phi{(\phi{(m)} ...
- [JZOJ NOIP2018模拟10.20 A组]
由于T3数据出锅,还不清楚自己的分数...估分150,前100已经拿到了,T3的50没拍过(写的就是暴力怎么拍),感觉很不稳 考试的时候就是特别的困,大概是因为早上在房间里腐败...腐败完了才睡觉 T ...
- [jzoj NOIP2018模拟10.23]
丢分主要是下面几个方面: 1.T2代码交错了,有个特判没写丢了10分 2.T1线段树加等差数列写错了(其实二维差分就可以,但我当时不会) 3.T3思考再三还是为了10分写上了主席树,还是写错了 总体评 ...
- [jzoj NOIP2018模拟10.29]
OI生涯的最高分,来了纪中这么多天,在经历了这么多场“NOIP难度”的模拟赛之后,终于看到了真正的NOIP 今天考场上效率很高,很快码完了全部的题目,留下了足够的时间对拍和...发呆.不得不说看着电脑 ...
- [JZOJ NOIP2018模拟10.21]
考试之前我刚刚领略到了特判的重要性,没想到T2的两个子任务还是写挂了,丢了20分 考试的感觉不行,一路打的都是暴力,正解的思路想到一半就断了推不下去 T1:逛公园 题目链接: https://jzoj ...
- [JZOJ NOIP2018模拟10.19]
T1写炸了今天,期望70却落了个20...连链上的都没有写对 T3什么什么线段树分治套AC自动机,表示我完全自闭了,幸好考场上没有杠T3 总体比赛还是比较舒服,暴力分给的蛮足的,不像昨天那样 T1:林 ...
- [NOIP2018模拟10.15]比赛报告
闲扯 昨晚又颓到好晚,Yali的降智光环感觉持续至今... 题面好评 T1T3都玩过 逃) T1没看多久就开始写二分+并查集 然后T3看着眼熟想了一个多小时...结果啥都没想出来 赶紧看T2发现还是没 ...
- JZOJ5895【NOIP2018模拟10.5】旅游
题目 Description
- jzoj5929. 【NOIP2018模拟10.26】情书
动态规划: #include<bits/stdc++.h> using namespace std; int n,iv[30]; #define mo 998244353 typedef ...
随机推荐
- excel破解工作簿与工作表保护
1.工作簿保护 1.1.使用压缩文件打开文件
- SDOI2018
SD的题有点反人类啊... d1t1[SDOI2018]物理实验 感觉比较好想但不太好写,写了一半弃了 d1t2[SDOI2018]战略游戏 建出圆方树,每次建虚树,答案就是虚树上的原点个数减去询问的 ...
- SpringCloud学习笔记(八):Zuul路由网关
概述 是什么? Zuul包含了对请求的路由和过滤两个最主要的功能: 其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础而过滤器功能则负责对请求的处理过程进行干预,是实现请 ...
- Docker系列(八):Kubernetes横空出世背后的秘密
Docker与CoreOS的恩怨情仇 2013年2月,Docker建立了一个网站发布它的首个演示版本, 3月,美国加州Alex Polvi正在自己的车库开始 他的 第二次创业 有了第一桶金的Alex这 ...
- sql还原数据库时候改变数据库名
需求:在做图书馆数据整合时候,由于有两个校区,用的是分离开的同一个数据库,数据库名字都一样的,现在我要整合在一起,我的想法是把两个数据库先还原到我本地,用写好的脚本整合到一起.所以,我还原两个数据库时 ...
- HTTP协议响应篇
http响应的基本介绍 一个HTTP响应代表服务器向客户端回送的数据, 由三个部分构成 状态行[200 , 302 304, 403, 404, 500] 响应消息头 返回的实体内容 http响应状态 ...
- 移动端适配(绝对单位、相对单位、CSS像素、物理像素、逻辑像素、设备像素比、PPI、Viewport)
在某公司做了一次分享,主题是‘移动端和pc端开发的区别’.可以说,这总结的原版就是在这样的契机下完成的.因为我只是习惯了移动端就应该那么写(设置viewport等),要是让我给大家分条讲下所以然,还真 ...
- 9个搜索引擎优化(SEO)最佳实践
作为网页设计师,搜索引擎优化重要吗?我们知道,网站设计是把屏幕上平淡无奇变成令人愉快的美感,更直观地辨认信息.这也是人与人之间在沟通想法,这样的方式一直在演变. 1. 网站结构 对于搜索引擎优化,网站 ...
- java编程规约二
四.OOP规约(Object Oriented Programming,面向对象设计) 1.静态变量和静态方法直接用类名访问,不要再new 对象去访问 2.方法覆盖必须加@Override注解 3.尽 ...
- POJ - 2778 ~ HDU - 2243 AC自动机+矩阵快速幂
这两题属于AC自动机的第二种套路通过矩阵快速幂求方案数. 题意:给m个病毒字符串,问长度为n的DNA片段有多少种没有包含病毒串的. 根据AC自动机的tire图,我们可以获得一个可达矩阵. 关于这题的t ...