[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 }
View Code

倍增法简介:

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 就是他们的最近公共祖先。

 

posted @ 2015-06-20 15:08  TonyFang  阅读(237)  评论(0编辑  收藏  举报