[ABC014D] 閉路
几乎是一道模板题。
读本篇题解需要了解的知识
-
什么是最近公共祖先。
-
重链剖分求最近公共祖先。
这两个知识点下文不再解释,没有掌握的同学请自行上网学习。
解法
题中给出一颗 n n n 个顶点的树,给出 q q q 组询问,每次询问给定 u u u 和 v v v,表示在顶点 u u u 和顶点 v v v 之间添加一条边,对于每次询问,输出包含顶点 u u u 和顶点 v v v 之间的边的环中边的个数。
关于树的性质:
-
一棵树上是没有环的。
-
在树上添加一条边肯定会生成一个环。
考虑我们在顶点 u u u 和顶点 v v v 之间添加一条边后生成的这个环,这个环由两部分构成,一部分是新添加的一条边,另一部分是在原来树上从 u u u 到 v v v 的简单路径。
再来看这个简单路径,这个路径包含了从 u u u 到两顶点的最近公共祖先的距离(这里即指简单路径长度,下文同)和 v v v 到两顶点的最近公共祖先的距离,举例如图所示,用 graph_editor 作图。
考虑 u = 5 , v = 3 u = 5,v = 3 u=5,v=3 的情况,在顶点 5 5 5 和 3 3 3 之间添加一条边,此时这个环的边的数量为 4 4 4。其中包括顶点 1 1 1(顶点 5 5 5 和 3 3 3 的最近公共祖先)到顶点 5 5 5 的距离,顶点 1 1 1 到顶点 3 3 3 的距离和新添加的这条边。
我们可以钦定一个点为这棵树的树根,从这个点开始两次深搜后得到最近公共祖先,两个点到它们的最近公共祖先的距离分别是两个点和它们的最近公共祖先的深度的差,所以对于每次询问我们只要输出 d e p u − d e p L C A + d e p v − d e p L C A + 1 = d e p u + d e p v − 2 × d e p L C A + 1 dep_u - dep_{LCA} + dep_v - dep_{LCA} + 1 = dep_u + dep_v - 2 \times dep_{LCA} +1 depu−depLCA+depv−depLCA+1=depu+depv−2×depLCA+1 即可。
代码
#include<bits/stdc++.h>
using namespace std;
#define Getchar() p1==p2 and (p2=(p1=Inf)+fread(Inf,1,1<<21,stdin),p1==p2)?EOF:*p1++
#define Putchar(c) p3==p4 and (fwrite(Ouf,1,1<<21,stdout),p3=Ouf),*p3++=c
char Inf[1<<21],Ouf[1<<21],*p1,*p2,*p3=Ouf,*p4=Ouf+(1<<21);
inline void read(int &x,char c=Getchar())
{
bool f=c!='-';
x=0;
while(c<48 or c>57) c=Getchar(),f&=c!='-';
while(c>=48 and c<=57) x=(x<<3)+(x<<1)+(c^48),c=Getchar();
x=f?x:-x;
}
inline void write(int x)
{
if(x<0) Putchar('-'),x=-x;
if(x>=10) write(x/10),x%=10;
Putchar(x^48);
}
int n,q,cnt,head[100010],dep[100010],f[100010],siz[100010],hvson[100010],start[100010];
struct edge
{
int to,next;
};
edge e[200010];
inline void add(const int &x,const int &y)
{
e[++cnt].to=y,e[cnt].next=head[x],head[x]=cnt;
}
inline void dfs1(int pos,int fa,int depth,int maxi=-0x3f3f3f3f)
{
dep[pos]=depth,f[pos]=fa,siz[pos]=1;
for(int i=head[pos];i;i=e[i].next)
{
if(e[i].to!=fa)
{
dfs1(e[i].to,pos,depth+1),siz[pos]+=siz[e[i].to];
if(siz[e[i].to]>maxi) hvson[pos]=e[i].to,maxi=siz[e[i].to];
}
}
}
inline void dfs2(int pos,int Start)
{
start[pos]=Start;
if(hvson[pos])
{
dfs2(hvson[pos],Start);
for(int i=head[pos];i;i=e[i].next)
if(e[i].to!=f[pos] && e[i].to!=hvson[pos]) dfs2(e[i].to,e[i].to);
}
}
inline int lca(int x,int y)
{
while(start[x]!=start[y])
if(dep[start[x]]>=dep[start[y]]) x=f[start[x]];
else y=f[start[y]];
if(dep[x]>=dep[y]) return y;
else return x;
}
int main()
{
read(n);
for(int i=1,x,y;i<n;i++) read(x),read(y),add(x,y),add(y,x);
read(q),dfs1(1,0,1),dfs2(1,1);
for(int i=1,x,y;i<=q;i++) read(x),read(y),write(dep[x]+dep[y]-2*dep[lca(x,y)]+1),Putchar('\n');
fwrite(Ouf,1,p3-Ouf,stdout),fflush(stdout);
return 0;
}