[POJ 1330] Nearest Common Ancestors (倍增法)
题目同上篇,最近公共祖先。
因为没有清零tot,RE了好多次TAT
一定要初始化啊!!
1 #include<cstdio> 2 #include<cstring> 3 #include<queue> 4 #include<iostream> 5 using namespace std; 6 int root,head[10010]; 7 int next[10010],to[10010],tot; 8 int deep[10010],n; 9 int p[10010][16]; 10 int deg[10010]; 11 inline void bfs(int root) { 12 queue<int> Q; 13 p[root][0]=root; deep[root]=0; 14 Q.push(root); 15 while(!Q.empty()) { 16 int u=Q.front(); Q.pop(); 17 for(int i=1;i<15;i++) 18 p[u][i]=p[p[u][i-1]][i-1]; 19 for(int i=head[u];i!=-1;i=next[i]) { 20 int v=to[i]; 21 deep[v]=deep[u]+1; p[v][0]=u; 22 Q.push(v); 23 } 24 } 25 } 26 inline int LCA(int x,int y) { 27 if(deep[x]<deep[y]) {int t=x;x=y;y=t;} 28 for(int i=0;i<15;i++) 29 if((deep[x]-deep[y]) & (1<<i)) x=p[x][i]; 30 if(x==y) return x; 31 for(int i=14;i>=0;i--) 32 if(p[x][i]!=p[y][i]) 33 x=p[x][i],y=p[y][i]; 34 return p[x][0]; 35 } 36 int main() { 37 int T; 38 scanf("%d",&T); 39 while(T--) { 40 tot=0; 41 memset(head,-1,sizeof(head)); memset(deg,0,sizeof(deg)); 42 scanf("%d",&n); 43 for (int i=1;i<=n-1;++i) { 44 int u,v; 45 scanf("%d%d",&u,&v); 46 to[tot]=v; 47 next[tot]=head[u]; 48 head[u]=tot++; 49 deg[v]=1; 50 } 51 for (int i=1;i<=n;++i) 52 if(! deg[i]) {root=i;break;} 53 bfs(root); 54 int u,v; 55 scanf("%d%d",&u,&v); 56 printf("%d\n",LCA(u,v)); 57 } 58 return 0; 59 }
倍增法简介:
deep[i] 表示 i节点的深度, fa[i,j]表示 i 的 2^j (即2的j次方) 倍祖先,那么fa[i , 0]即为节点i 的父亲,然后就有一个递推式子fa[i,j]=fa [fa[i,j-1],j-1]
可以这样理解:设tmp = fa [i, j - 1] ,tmp2 = fa [tmp, j - 1 ] ,即tmp 是i 的第2 ^ (j - 1) 倍祖先,tmp2 是tmp 的第2 ^ (j - 1) 倍祖先,
所以tmp2 是i 的第2 ^ (j - 1) + 2 ^ (j - 1) = 2^ j 倍祖先
注意:这里的“倍”可不能理解为倍数的意思,而是距离节点i有多远的意思,节点i的第2 ^ j 倍祖先表示的节点u满足deep[ u ] - deep[ i ] = 2 ^ j。
这样子一个O(NlogN)的预处理求出每个节点的 2^k 的祖先
然后对于每一个询问的点对a, b的最近公共祖先就是:
先判断是否 d[x]< d[y] ,如果是的话就交换一下(保证 x 的深度大于 y 的深度), 然后把 x 调到与 y 同深度, 同深度以后再把a, b 同时往上调,调到有一个最小的 j 满足fa [x,j] != fa [y,j] (x,y是在不断更新的), 最后再把(x,y)往上调(x=p[x,0], y=p[y,0]) ,一个一个向上调直到x = y, 这时 x或y 就是他们的最近公共祖先。
这篇文章由TonyFang发布。
所有解释权归TonyFang所有。
Mailto: tony-fang@map-le.net