题意要求一棵树上,两个点的最近公共祖先 即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\n",ans[findset(T)]);return;}}elseif (u==T){if (vis[S]){printf("%d\n",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;
}