小X的佛光 NOIP模拟赛 倍增LCA 树结构
Posted JayWang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了小X的佛光 NOIP模拟赛 倍增LCA 树结构相关的知识,希望对你有一定的参考价值。
题面与官方std详解在最下方。
题意:给出一颗N个节点、N-1条边的无向图(树),给出Q个询问,每个询问有两条路径,求路径覆盖点的个数。其中Nmax=Qmax=200000
思路:
对于在树上的路径,我们可以用LCA解决。
举个栗子,若A与B结点的LCA是C,那么LAB=LAC+LBC。当边权都是1的时候,这个式子又可以化为:LAB=Cdep-Adep+Cdep-Bdep,读者可以自行画图验证。
怎么用?
对于两条给定路径,求公共部分的结点数,既可以转化为求公共部分的边数+1。
那么对于题目给出的两条确定路径AB、BC,可以知道公共结点数=(LAB+LBC-LAC)/2+1,读者可以自行画图验证。
这就好办了,只需要写一个树上倍增求LCA即可。
其他需要注意的事情
本题Nmax=200000,如果用dfs求深度和父亲节点,会爆栈。
怎么解决?
1、换用bfs求解上述参数。
2、扩栈(在编译器参数后面加上-Wl,--stack=256000000)(意思是把栈扩展到256MB)
AC代码
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 template<class T> inline void read(T &_a){ 6 bool f=0;int _ch=getchar();_a=0; 7 while(_ch<\'0\' || _ch>\'9\'){if(_ch==\'-\')f=1;_ch=getchar();} 8 while(_ch>=\'0\' && _ch<=\'9\'){_a=(_a<<1)+(_a<<3)+_ch-\'0\';_ch=getchar();} 9 if(f)_a=-_a; 10 } 11 12 const int maxn=200001; 13 struct edge 14 { 15 int to,next; 16 }w[maxn<<1]; 17 int n,Q,num,egcnt,head[maxn],fa[maxn][20],ans,dep[maxn]; 18 bool vis[maxn]; 19 20 inline void __SUPER_addedge(int from,int to) 21 { 22 w[++egcnt].to=to; 23 w[egcnt].next=head[from]; 24 head[from]=egcnt; 25 w[++egcnt].to=from; 26 w[egcnt].next=head[to]; 27 head[to]=egcnt; 28 } 29 30 void rushA(int u,int fr) 31 { 32 fa[u][0]=fr; dep[u]=dep[fr]+1; 33 for (register int i=head[u];i;i=w[i].next) if(w[i].to!=fr) rushA(w[i].to,u); 34 } 35 36 inline void rushB() 37 { 38 for (register int i=1;i<=19;++i) 39 for (register int v=1;v<=n;++v) 40 fa[v][i]=fa[fa[v][i-1]][i-1]; 41 } 42 43 inline int LCA(int a,int b) 44 { 45 if(dep[a]<dep[b]) swap(a,b); 46 for (register int i=19;i>=0&&dep[a]>dep[b];--i) 47 if(dep[a]-(1<<i)>=dep[b]) a=fa[a][i]; 48 for (register int i=19;i>=0;--i) 49 if(fa[a][i]!=fa[b][i]) a=fa[a][i],b=fa[b][i]; 50 return a==b?a:fa[a][0]; 51 } 52 53 int main() 54 { 55 freopen("light.in","r",stdin); 56 freopen("light.out","w",stdout); 57 read(n); read(Q); read(num); 58 for (register int i=1,x,y;i<n;++i) read(x),read(y),__SUPER_addedge(x,y); 59 rushA(1,0); rushB(); 60 for (register int a,b,c;Q;--Q) 61 { 62 read(a); read(b); read(c); 63 int l1=LCA(a,b),l2=LCA(b,c),l3=LCA(a,c); 64 int s1=dep[a]-dep[l1]+dep[b]-dep[l1]; 65 int s2=dep[b]-dep[l2]+dep[c]-dep[l2]; 66 int s3=dep[a]-dep[l3]+dep[c]-dep[l3]; 67 printf("%d\\n",(s1+s2-s3)/2+1); 68 } 69 return 0; 70 }
小X是远近闻名的学佛,平日里最喜欢做的事就是蒸发学水。
小X所在的城市X城是一个含有N个节点的无向图,同时,由于X国是一个发展中国家,为了节约城市建设的经费,X国首相在建造X城时只建造N–1条边,使得城市的各个地点能够相互到达。
小X计划蒸发Q天的学水,每一天会有一名学水从A地走到B地,并在沿途各个地点留下一个水塘。此后,小X会从C地走到B地,并用佛光蒸发沿途的水塘。由于X城是一个学佛横行的城市,学水留下的水塘即使没有被小X蒸发,也会在第二天之前被其他学佛蒸发殆尽。
现在,小X想要知道,他每一天能够蒸发多少水塘呢?
输入格式
第一行三个整数N,Q,num,分别表示X城地点的个数,小X蒸发学水的天数,以及测试点编号。注意,测试点编号是为了让选手们更方便的获得部分分,你可能不需要用到这则信息,在下发的样例中,测试点编号的含义是该样例满足某一测试点限制。
接下来N–1行,每行两个整数X,Y,表示X地与Y地之间有一条边。
接下来Q行,每行三个整数A,B,C,表示一天中,有一名学水从A地走到B地,而小X会从C地走到B地。
输出格式
输出Q行,每行一个整数,表示小X能够蒸发的水塘数。
样例输入
3311223123113313样例输出
113数据规模与约定
特殊性质1:第i条边连接第i和第i+1个地点。
特殊性质2:A=C。
官方STD题解:
注意到问题实际上是问树上一个点到另外两点路径的重合长度,也就是说,这两条路径有一个端点是重合的,不妨就此性质构建算法。
树上三点,或是呈一条链的形式,或是存在一个中心,使得中心到三点之间的路径不相交。如下图,上方的两种情况是A、B、C点存在中心O,下方两种情况是A、B、C点在一条链上。对于呈一条链的形式,我们不妨将三个点中,位于中间的那个点称为三个点的中心,如此一来,我们要求的答案实际上是中心到B点的距离。那么,如何求中心呢?通过观察上图,我们发现,中心一定是A、B、C点中两点的LCA。并且,通过进一步观察,我们可以发现,中心是A、B、C点两两的LCA中,深度最大的点。所以,我们只需求出中心,在求出中心到B点的距离即可解决本题。该算法的时间复杂度是O(N+Q)或O((N+Q)*LogN)的。
以上是关于小X的佛光 NOIP模拟赛 倍增LCA 树结构的主要内容,如果未能解决你的问题,请参考以下文章
$Noip2013/Luogu1967$ 货车运输 最大生成树+倍增$lca$
洛谷 P2680 [NOIP2015 提高组] 运输计划(二分,树上查分,树上倍增,LCA)