题意要求一棵树上,两个点的最近公共祖先 即LCA
现学了一下LCA-Tarjan算法,还挺好理解的,这是个离线的算法,先把询问存贮起来,在一遍dfs过程中,找到了对应的询问点,即可输出
原理用了并查集和dfs染色,先dfs到底层开始往上回溯,边并查集合并 一边染色,这样只要询问的两个点均被染色了,就可以输出当前并查集的最高父亲一定是LCA,因为我是从底层层层往上DSU和染色的,要么没被染色,被染色之后,肯定就是当前节点是最近的
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; const int N = 10000+10; int f[N],pre[N],vis[N]; int ans[N]; vector<int> v[N]; int n,S,T; void init() { for (int i=0;i<=n+1;i++){ f[i]=i; v[i].clear(); vis[i]=0; pre[i]=-1; } } int findset(int x) { if (x!=f[x]){ f[x]=findset(f[x]); } return f[x]; } int unionset(int a,int b) { int r1=findset(a); int r2=findset(b); if (r1==r2) return r1; f[r2]=r1; return r1; } void LCA(int u) { ans[u]=u; for (int i=0;i<v[u].size();i++){ int vx=v[u][i]; LCA(vx); int rt=unionset(u,vx); ans[rt]=u; } vis[u]=1; if (u==S){ if (vis[T]){ printf("%d ",ans[findset(T)]); return; } } else if (u==T){ if (vis[S]){ printf("%d ",ans[findset(S)]); return; } } } int main() { int t,a,b; scanf("%d",&t); while (t--){ scanf("%d",&n); init(); for (int i=1;i<n;i++){ scanf("%d%d",&a,&b); v[a].push_back(b); pre[b]=a; } scanf("%d%d",&S,&T); for (int i=1;i<=n;i++){ if (pre[i]==-1){ LCA(i); break; } } } return 0; }