题目链接: poj 1470
题目大意: 给出一棵树,然后有有限次查询(a,b)
每次查询节点a与节点b的最近公共祖先
输出节点作为最近公共祖先的次数
解题思路: 离线查询最近公共祖先
把每次查到的结果visit[ ]++,最后遍历一遍就行了
代码:
//Final LCA离线算法求最近公共祖先 #include <stdio.h> #include <string.h> #include <stdlib.h> #include <algorithm> #include <vector> using namespace std; #define MAX 1000 vector<int> Hash[MAX],Qes[MAX]; int n,ansetor[MAX],visit[MAX],parent[MAX],fathernum[MAX],num[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(x!=s) { 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) { int i,size; size=Hash[u].size(); ansetor[u]=u; for(i=0;i<size;i++) //size()从0开始计算 { LCA(Hash[u][i]); Union(u,Hash[u][i]); ansetor[Find(u)]=u; //***可以是Find(u)或者Find(Hash[u][i]),因为已经合并了 } visit[u]=1; size=Qes[u].size(); for(i=0;i<size;i++) //size()从0开始计算 { if(visit[Qes[u][i]]) //如果需要查找的两个点其中一个点之前被访问过, { //那么此时它的祖先就是它们的最近公共祖先 num[ansetor[Find(Qes[u][i])]]++; //***只能是Find(Qes[u][i]),因为此时u和Qes[u][i]并未合并 } } } int main() { int i,j,a,b,c,m,k=0; char ch,ch1; while(scanf("%d",&n)!=EOF) { for(i=1;i<=n;i++) { Hash[i].clear(); Qes[i].clear(); } k=0; Init(n); memset(visit,0,sizeof(visit)); memset(ansetor,0,sizeof(ansetor)); memset(num,0,sizeof(num)); memset(fathernum,0,sizeof(fathernum)); while(1) { scanf("%d%c",&a,&ch); if(ch!=':') { m=a; break; } scanf("%c%d%c",&ch1,&b,&ch1); for(j=1;j<=b;j++) { scanf("%d",&c); Hash[a].push_back(c); //表示a是b的父亲 fathernum[c]++; //记录每个顶点父亲的个数 } getchar(); } while(scanf("%c",&ch)!=EOF) { if(ch=='(') { scanf("%d%d",&a,&b); getchar(); k++; Qes[a].push_back(b); //需要查找的两点 Qes[b].push_back(a); //需要查找的两点 if(k>=m) { break; } } } for(i=1;i<=n;i++) { if(!fathernum[i]) //没有父亲结点的点既是整棵树的根节点 { LCA(i); break; } } for(i=1;i<=n;i++) { if(num[i]) printf("%d:%d\n",i,num[i]); } } return 0; }