题目链接: poj 1330
题目大意: 给出你一棵树,最后一行询问顶点a和顶点b的最近公共祖先
解题思路: Tarjan离线查找最近公共祖先:
搜到新的顶点,此顶点的临时祖先就是上一层的顶点
直到搜到叶子就开始回溯,回溯的时候
从这点出发搜过的顶点的临时祖先合并为这个顶点的上一层顶点
代码:
//Final LCA离线算法求最近公共祖先 #include <stdio.h> #include <string.h> #include <stdlib.h> #include <algorithm> #include <vector> #define MAX 11000 using namespace std; vector<int>Hash[MAX],Qes[MAX]; int n,m,visit[MAX],ansetor[MAX],parent[MAX],fathernum[MAX]; void Init(int n) //并查集初始化 { for(int i=1;i<=n;i++) parent[i]=i; } int Find(int x) //并查集查找和压缩路径 { int s,j; s=x; while(x!=parent[x]) x=parent[x]; while(s!=x) { j=parent[s]; parent[s]=x; s=j; } return x; } void Union(int r1,int r2) //并查集合并 { int R1,R2; R1=Find(r1); R2=Find(r2); if(R1!=R2) parent[R1]=R2; } void LCA(int u) //LCA { int i,size; visit[u]=1; ansetor[u]=u; size=Hash[u].size(); for(i=0;i<size;i++) //size()从0开始计算 { if(!visit[Hash[u][i]]) { LCA(Hash[u][i]); Union(u,Hash[u][i]); ansetor[Find(Hash[u][i])]=u; } //***可以是Find(u)或者Find(Hash[u][i]),因为已经合并了 } size=Qes[u].size(); for(i=0;i<size;i++) //size()从0开始计算 { if(visit[Qes[u][i]]) //如果需要查找的两个点其中一个点之前被访问过, //那么此时它的祖先就是它们的最近公共祖先 { m=ansetor[Find(Qes[u][i])]; //***只能是Find(Qes[u][i]),因为此时u和Qes[u][i]并未合并 return ; } } } int main() { int a,b,i,t; scanf("%d",&t); while(t--) { scanf("%d",&n); Init(n); memset(visit,0,sizeof(visit)); memset(fathernum,0,sizeof(fathernum)); for(i=1;i<=n;i++) { Qes[i].clear(); Hash[i].clear(); } for(i=1;i<=n-1;i++) { scanf("%d%d",&a,&b); Hash[a].push_back(b); //表示a是b的父亲 fathernum[b]++; //记录每个顶点父亲的个数 } scanf("%d%d",&a,&b); Qes[a].push_back(b); //需要查找的两点 Qes[b].push_back(a); //需要查找的两点 for(i=1;i<=n;i++) { if(!fathernum[i]) //没有父亲结点的点既是整棵树的根节点 { LCA(i); printf("%d\n",m); break; } } } return 0; }