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 */
然后是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 }
有一点要注意的: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 }