水一发题解。
我只是想存一下树剖LCA的代码......
以洛谷上的这个模板为例:P3379 【模板】最近公共祖先(LCA)
1.朴素LCA
就像做模拟题一样,先dfs找到基本信息:每个节点的父亲、深度。
把深的节点先往上跳。
深度相同了之后,一起往上跳。
最后跳到一起了就是LCA了。
预处理:O(n)
每次查询:O(n)
2.倍增LCA
朴素LCA的一种优化。
一点一点跳,显然太慢了。
如果要跳x次,可以把x转换为二进制。
每一位都是1或0,也就是跳或者不跳。
在第i位,如果跳,就向上跳2(i-1)次。
至于跳或者不跳,判断很简单。
如果跳了之后还没在一起,就跳。
预处理:算出每个点上跳2n次后的位置。(已知上跳20次的位置就是它的父亲)O(nlogn)
每次询问:O(logn)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 6 int n,m,s; 7 int hd[500005],nx[1000005],to[1000005],cnt; 8 9 void add(int af,int at) 10 { 11 to[++cnt]=at; 12 nx[cnt]=hd[af]; 13 hd[af]=cnt; 14 } 15 16 int d[500005],f[500005][25]; 17 18 void pre(int p,int fa) 19 { 20 f[p][0]=fa; 21 d[p]=d[fa]+1; 22 for(int i=hd[p];i;i=nx[i]) 23 { 24 if(to[i]!=fa)pre(to[i],p); 25 } 26 } 27 28 int lca(int x,int y) 29 { 30 if(d[x]<d[y])swap(x,y); 31 for(int i=20;i>=0;i--) 32 { 33 if(d[f[x][i]]>=d[y])x=f[x][i]; 34 } 35 if(x==y)return x; 36 for(int i=20;i>=0;i--) 37 { 38 if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i]; 39 } 40 return f[x][0]; 41 } 42 43 int main() 44 { 45 scanf("%d%d%d",&n,&m,&s); 46 for(int i=1;i<n;i++) 47 { 48 int aa,bb; 49 scanf("%d%d",&aa,&bb); 50 add(aa,bb); 51 add(bb,aa); 52 } 53 pre(s,0); 54 for(int i=1;i<=20;i++) 55 { 56 for(int j=1;j<=n;j++) 57 { 58 f[j][i]=f[f[j][i-1]][i-1]; 59 } 60 } 61 for(int i=1;i<=m;i++) 62 { 63 int x,y; 64 scanf("%d%d",&x,&y); 65 printf("%d\n",lca(x,y)); 66 } 67 return 0; 68 }