在求解最近公共祖先为问题上,用到的是Tarjan的思想,从根结点开始形成一棵深搜树,处理技巧就是在回溯到结点u的时候,u的子树已经遍历,这时候才把u结点放入合并集合中,这样u结点和所有u的子树中的结点的最近公共祖先就是u了,u和还未遍历的所有u的兄弟结点及子树中的最近公共祖先就是u的父亲结点。这样我们在对树深度遍历的时候就很自然的将树中的结点分成若干的集合,两个集合中的所属不同集合的任意一对顶点的公共祖先都是相同的,也就是说这两个集合的最近公共祖先只有一个。时间复杂度为O(n+q),n为节点,q为询问节点对数。
#include"stdio.h" #include"string.h" #include"vector" using namespace std; #define N 11000 const int inf=1<<20; vector<int>g[N]; int s,t,n; int f[N],pre[N],ans[N]; bool vis[N]; int findset(int x) { if(x!=f[x]) f[x]=findset(f[x]); return f[x]; } int unionset(int a,int b) { int x=findset(a); int y=findset(b); if(x==y) return x; f[y]=x; return x; } void lca(int u) { int i,v; ans[u]=u; for(i=0;i<g[u].size();i++) { v=g[u][i]; lca(v); int x=unionset(u,v); ans[x]=u; } vis[u]=1; if(u==s&&vis[t]) { printf("%d\n",ans[findset(t)]); return ; } else if(u==t&&vis[s]) { printf("%d\n",ans[findset(s)]); return ; } } int main() { int i,u,v,T; scanf("%d",&T); while(T--) { scanf("%d",&n); for(i=1;i<=n;i++) { pre[i]=-1; //记录节点I的父节点 f[i]=i; //并查集记录根节点 vis[i]=0; g[i].clear(); } for(i=1;i<n;i++) { scanf("%d%d",&u,&v); g[u].push_back(v); pre[v]=u; } scanf("%d%d",&s,&t); for(i=1;i<=n;i++) if(pre[i]==-1) break; lca(i); } return 0; }
LCA 算法学习 (最近公共祖先)poj 1330,布布扣,bubuko.com
时间: 2024-10-11 05:05:57