其实这个问题蒟蒻感觉,就是模拟,只不过,在模拟之前需要一些预处理。
先谈谈倍增,其实就是对于寻找公共祖先,如果深度差距太大,就需要进行很多没有必要的计算:比如要跳4步,一步一步跳要4次,而我四步四步跳只要一次(当然有人人为我这么说太凑巧了,这里只是为了说明问题,并不是真实情况),那如果数据再大一点呢?倍增就显现出优势啦~。
预处理:
定义f[x][y]为从x->y需要跳2^y步,那么自然有f[x][y+1]=f [ f [ x ] [ y ] ] [ y ]。而且维护dep[]数组和f数组(f[v][0]=u)。
void Deal_first(int u,int fa) { dep[u]=dep[fa]+1; for(int i=0; i<=19; i++) f[u][i+1]=f[f[u][i]][i]; for(int j=head[u]; j; j=nxt[j]) { int v=to[j]; if(v==fa) continue; f[v][0]=u; Deal_first(v,u); } }
利用链式前向星,一直搜下去,并维护dep(别忘了我们之后是要改变深度的啊)。
LCA本体:(简单易懂,就不解释了)
int LCA(int x,int y) { if(dep[x]<dep[y]) swap(x,y); for(int i=20; i>=0; i--) { if(dep[f[x][i]]>=dep[y]) x=f[x][i]; if(x==y)//如果x,y在同一个分支上,直接返回就好 return x; } for(int i=20; i>=0; i--) { if(f[x][i]!=f[y][i]) { x=f[x][i]; y=f[y][i]; } } return f[x][0]; }
最后是完整代码:
#include<cstdio> #include<iostream> using namespace std; #define maxn 500005 int n,m,s; int to[2*maxn],head[2*maxn],nxt[2*maxn],cnt; int dep[maxn],f[maxn][21]; void add(int a,int b) { cnt++; to[cnt]=b; nxt[cnt]=head[a]; head[a]=cnt; } void Deal_first(int u,int fa) { dep[u]=dep[fa]+1; for(int i=0; i<=19; i++) f[u][i+1]=f[f[u][i]][i]; for(int j=head[u]; j; j=nxt[j]) { int v=to[j]; if(v==fa) continue; f[v][0]=u; Deal_first(v,u); } } int LCA(int x,int y) { if(dep[x]<dep[y]) swap(x,y); for(int i=20; i>=0; i--) { if(dep[f[x][i]]>=dep[y]) x=f[x][i]; if(x==y)//如果x,y在同一个分支上,直接返回就好 return x; } for(int i=20; i>=0; i--) { if(f[x][i]!=f[y][i]) { x=f[x][i]; y=f[y][i]; } } return f[x][0]; } int main() { scanf("%d%d%d",&n,&m,&s); int a,b; for(int i=1; i<n; i++) { scanf("%d%d",&a,&b); add(a,b); add(b,a); } Deal_first(s,0); for(int i=1; i<=m; i++) { scanf("%d%d",&a,&b); int ans=LCA(a,b); printf("%d ",ans); } return 0; }