这就是一道很朴素的LCA题。
算法应该主流的有三种:DFS序+RMQ;倍增;Tarjan;
其中前面两个是在线算法,O(n log n),后一个是离线的,复杂度也是线性的O(n+q)。
所以对于这道题n,q都偏大的情况下还是选择了Tarjan(后两种我不会)。
简单说一下Tarjan的思想,对整个树进行一次DFS,vis数组记录这个点是否被记录。
对于每一个搜到的点,先查询所有要问的与它有关的点(就比如要求i,j,在搜到i或j的时候就要找到j或i),如果这个点被访问过了他们的LCA就是这个点的father(用并查集实现)。
Why——?
因为如果这两个点都访问过了,那它们肯定在同一棵子树中(以根节点也算子树),那么由于j先被访问,因此它的祖先就是i,j的LCA。
最后注意一下这是一棵树,所以总边数是n-1。
提示一下在大牛分站交会有O2优化。
CODE
#include<cstdio> #include<vector> using namespace std; const int N=500005; vector <int> a[N],q[N],num[N]; int father[N],ans[N],n,m,x,y,i,root; bool vis[N]; inline void read(int &x) { x=0; char ch=getchar(); while (ch<'0'||ch>'9') ch=getchar(); while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); } inline void write(int x) { if (x/10) write(x/10); putchar(x%10+'0'); } inline int getfather(int k) { return father[k]==k?k:father[k]=getfather(father[k]); } inline void LCA(int k) { vis[k]=1; for (int i=0;i<q[k].size();++i) { int x=q[k][i]; if (vis[x]) ans[num[k][i]]=getfather(x); } for (int i=0;i<a[k].size();++i) { int x=a[k][i]; if (!vis[x]) LCA(x),father[x]=k; } } int main() { read(n); read(m); read(root); for (i=1;i<=n;++i) father[i]=i; for (i=1;i<=n-1;++i) { read(x); read(y); a[x].push_back(y); a[y].push_back(x); } for (i=1;i<=m;++i) { read(x); read(y); q[x].push_back(y); num[x].push_back(i); q[y].push_back(x); num[y].push_back(i); } LCA(root); for (i=1;i<=m;++i) write(ans[i]),putchar(' '); return 0; }