学习笔记:树剖求 LCA
我之前介绍过 tarjan 求 LCA 的方法:链接-->戳我!
然而离线算法又长又臭,果真烦人,于是我们尝试用树剖解决问题。
我们联想暴力求 LCA 的方法,发现在跳父亲的时候有一些点可以一下子跳过去。
而树剖便是把链们作为一个整体,由于 \(top\) 数组的存在,直接跳到 \(f_{top}\) 即可。
复杂度容易证明是 \(\mathcal O((n+m)\log n)\),常数比倍增小,不算很难写(至少我一遍 \(A\) 了
\(Code\):
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
#define read(x) scanf("%d",&x)
#define MAXN 500005
int top[MAXN],dep[MAXN],son[MAXN],f[MAXN],tot[MAXN];
struct node
{
int to,nxt;
}e[MAXN<<1];
int head[MAXN],cnt=0;
int n,m,root,l,r;
void add(int u,int v)
{
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
int dfsa(int cur,int deep)
{
dep[cur]=deep;
tot[cur]=1;
son[cur]=0;
int maxn=0;
for(int i=head[cur];i;i=e[i].nxt)
{
int j=e[i].to;
if(j==f[cur]) continue;
f[j]=cur;
int now=dfsa(j,deep+1);
if(now>maxn) maxn=now,son[cur]=j;
tot[cur]+=now;
}
return tot[cur];
}
void dfsb(int cur,int topf)
{
top[cur]=topf;
if(son[cur]) dfsb(son[cur],topf);
for(int i=head[cur];i;i=e[i].nxt)
{
int j=e[i].to;
if(top[j]) continue;
dfsb(j,j);
}
return;
}
int lca(int x,int y)
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
x=f[top[x]];
}
return dep[x]<dep[y]?x:y;
}
int main()
{
read(n),read(m),read(root);
for(int i=1;i<n;i++)
{
read(l),read(r);
add(l,r),add(r,l);
}
f[root]=root;
dfsa(root,1);
dfsb(root,root);
for(int i=1;i<=m;i++)
{
read(l),read(r);
printf("%d\n",lca(l,r));
}
return 0;
}