poj 1330 Nearest Common Ancestors(LCA:最近公共祖先)

多校第七场考了一道lca,那么就挑一道水题学习一下吧= =

最简单暴力的方法:建好树后,输入询问的点u,v,先把u全部的祖先标记掉,然后沿着v->rt(根)的顺序检查,第一个被u标记的点即为u,v的公共祖先。

标记的时候又犯老毛病了:while,do while都不对,最后还是while(1)了T^T

 1 #include<cstdio>
 2 #include<cstring>
 3 
 4 const int MAXN=11111;
 5 
 6 int p[MAXN],vis[MAXN];
 7 
 8 int main()
 9 {
10     int T,n,u,v;
11     scanf("%d",&T);
12     while(T--)
13     {
14         scanf("%d",&n);
15         memset(vis,0,sizeof(vis));
16         memset(p,-1,sizeof(p));
17         for(int i=0;i<n-1;i++){
18             scanf("%d%d",&u,&v);
19             p[v]=u;
20         }
21         scanf("%d%d",&u,&v);
22         while(1)
23         {
24             vis[u]=1;
25             if(p[u]==-1)
26                 break;
27             u=p[u];
28         }
29         while(vis[v]!=1)
30             v=p[v];
31         printf("%d\n",v);
32     }
33     return 0;
34 }
35 /*
36 10
37 3
38 1 2
39 1 3
40 2 3
41 */
View Code

然后是tarjin了:

自己先照着推了一遍代码,才把图示给看明白:http://www.csie.ntnu.edu.tw/~u91029/LowestCommonAncestor.html

可以处理多组询问

因为是以dfs为框架,所以先处理子树,依次将当前点u的每棵子树加入当前点的集合,之后对当前点进行询问(u,v)。若v已被标记,由于是dfs,所以最近公共祖先包含v的子树必然已经完成了搜索,那么v所在集合的祖先 find(v) 就是询问(u,v)的解。又因为是dfs,所以子树内的询问必然已经完成。

注意:询问(u,v)要正反向各加一遍,因为标记vis有先后顺序,而我们不清楚u,v的被访问的顺序。搜索到u时v尚未被标记,会放弃此次询问。所以不必担心同一次询问被完成了两次,其中一次会被舍弃。

关键是要理解先处理dfs,再合并集合。

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<vector>
  4 #define clr(a,m) memset(a,m,sizeof(a))
  5 #define rep(i,a,b) for(int i=a;i<=b;i++)
  6 using namespace std;
  7 
  8 const int MAXN=11111;
  9 
 10 struct Edge{
 11     int v,next;
 12     Edge(){}
 13     Edge(int _v,int _next):v(_v),next(_next){}
 14 }edge[MAXN<<1];
 15 
 16 vector<int>query[MAXN];
 17 
 18 int p[MAXN],vis[MAXN],ancestor[MAXN];
 19 int head[MAXN],tol;
 20 
 21 void init(int n)
 22 {
 23     tol=0;
 24     clr(head,-1);
 25 
 26     rep(i,1,n){
 27         query[i].clear();
 28     }
 29 }
 30 
 31 void add(int u,int v)
 32 {
 33     edge[tol]=Edge(v,head[u]);
 34     head[u]=tol++;
 35 }
 36 
 37 int build(int n)
 38 {
 39     int u,v;
 40     init(n);
 41     clr(vis,0);
 42     rep(i,1,n-1){
 43         scanf("%d%d",&u,&v);
 44         vis[v]=1;
 45         add(u,v);
 46     }
 47     rep(i,0,0){
 48         scanf("%d%d",&u,&v);
 49         query[u].push_back(v);
 50         query[v].push_back(u);
 51     }
 52     rep(i,1,n)
 53         if(!vis[i])
 54             return i;
 55 }
 56 
 57 int find(int x)
 58 {
 59     return (x==p[x])?x:(p[x]=find(p[x]));
 60 }
 61 
 62 void dfs(int x)
 63 {
 64     vis[x]=1;
 65     for(int i=head[x];i!=-1;i=edge[i].next)
 66     {
 67         int v=edge[i].v;
 68         dfs(v);
 69         p[v]=x;
 70     }
 71     
 72     int siz =query[x].size()-1;
 73     rep(i,0,siz)
 74     {
 75         if(vis[query[x][i]]){
 76             printf("%d\n",find(query[x][i]));
 77             return ;
 78         }
 79     }
 80 }
 81 
 82 void LCA(int rt,int n)
 83 {
 84     clr(vis,0);
 85     rep(i,1,n)
 86         p[i]=i;
 87     dfs(rt);
 88 }
 89 
 90 int main()
 91 {
 92     int T,n,u,v;
 93     scanf("%d",&T);
 94     while(T--)
 95     {
 96         scanf("%d",&n);
 97         int rt=build(n);
 98         LCA(rt,n);
 99     }
100     return 0;
101 }
View Code

有一点要注意的:query[x].size()的返回值竟然不是 int 型的= = ,不信可以把 siz 省略掉,在循环内打印一下,惊喜的发现 for(int i=0;i<-1;i++) 这种东西竟然能够进入循环。强制转换(int)也能解决。

再附上一段代码,是实现全部询问的,当然不适合这道题,MAXN=11111,MLE我竟然还反应了半天。。

 1 void dfs(int x,int n)
 2 {
 3     vis[x]=1;
 4     for(int y=head[x];y!=-1;y=edge[y].next)
 5     {
 6         int v=edge[y].v;
 7         dfs(v,n);
 8         p[v]=x;
 9     }
10     rep(y,1,n)
11         if(vis[y])
12             lca[x][y]=lca[y][x]=find(y);
13 }
View Code

 

posted @ 2013-08-14 17:05  Thousand Sunny  阅读(192)  评论(0编辑  收藏  举报