lca最暴力朴素的求法就是x,y一次次往上跳, 这样效率显然很低
考虑优化, 有两种途经:
- dfs预处理一个倍增找父亲节点的数组, x,y成倍的往上跳, 时间复杂度降为log级
- 利用树链剖分把树处理成一些链, 每次调到当前链的链顶, 直至x,y在同一条链上, 复杂度log级(一共不超过logn条重链)
详见代码
倍增
#include<cstdio>
#define re register
#define in inline
in int read()
{
int s=0,b=1;
char ch;
do{
ch=getchar();
if(ch=='-') b=-1;
}while(ch<'0' || ch>'9');
while(ch>='0' && ch<='9')
{
s=s*10+ch-'0';
ch=getchar();
}
return b*s;
}
int n,m,s;
struct edge{
int t,next;
}e[500001*2];
int cnt=0,head[500001];
in void add(int f,int t)
{
++cnt;
e[cnt].t=t;
e[cnt].next=head[f];
head[f]=cnt;
}
int f[500001][100],d[500001]; //f[i][j]: i号节点往上走2^j次的父亲编号. d[i]: i号节点的深度(root深度为1)
int log[500001];
void dfs(int now,int fa)
{
d[now]=d[fa]+1;
f[now][0]=fa;
for(re int i=1;(1<<i)<=d[now];++i)//注意不能跳到root上面去了
f[now][i]=f[f[now][i-1]][i-1]; //预处理出所有倍增跳法的结果, 利用now的2^i辈父亲 = 2^(i-1)辈父亲的2^(i-1)辈父亲, 而now的2^(i-1)辈父亲之前已经算出来. 类似于递推.
for(re int i=head[now];i;i=e[i].next)
if(e[i].t!=fa) dfs(e[i].t,now);
}//Dfs预处理出f数组, 便于一会儿倍增地向上跳跃. 顺便求d数组.
in int lca(int x,int y)
{
if(d[x]<d[y])
{
int t=x;
x=y;
y=t;
}//保证x在y下
while(d[x]!=d[y]) x=f[x][log[d[x]-d[y]]]; //x先跳到与y同深度. 这时可以根据f数组倍增地向上跳(利用了每个数都可以写成二进制), 比如本来要跳13(二进制: 1101)下, 现在可以先跳2^3下, 再跳2^2下, 再跳2^0下.
if(x==y) return x;
for(re int i=log[d[x]];i>=0;--i)
if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; //同理, x y同步倍增地向上跳, 直至它们有相同的父亲. 不能跳到x==y, 因为公共祖先为root时这样判断会有玄学错误.
return f[x][0]; //x y的父亲即公共祖先.
}
int main()
{
n=read();
m=read();
s=read();
int x,y;
int t=1;
log[1]=0;
for(re int i=2;i<=n;++i)
{
if((1<<(t+1))==i) t++;
log[i]=t;
}//预处理出log_2(1~n)的值;
for(re int i=1;i<=n-1;++i)
{
x=read();
y=read();
add(x,y);
add(y,x);
}
dfs(s,0);
for(re int i=1;i<=m;++i)
{
x=read();
y=read();
printf("%d\n",lca(x,y));
}
return 0;
}
树剖
//sorry, 会树剖就是可以为所欲为.jpg
#include<cstdio>
#define in inline
#define re register
#define ll long long
in int read()
{
int s=0,b=0;char ch;
do{ch=getchar();if(ch=='-') b=1;}while(ch<'0'||ch>'9');
while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+(ch^48);ch=getchar();}
return b?-s:s;
}
const int N=500001;
int n,m,root;
struct edge{
int t,nxt;
}e[N*2];
int cnt,head[N];
int id[N],f[N],d[N],subt[N],son[N],top[N];
in void add(int f,int t)
{
++cnt;
e[cnt].t=t,e[cnt].nxt=head[f],head[f]=cnt;
++cnt;
e[cnt].t=f,e[cnt].nxt=head[t],head[t]=cnt;
}
void dfs1(int now,int fa)
{
d[now]=d[fa]+1,f[now]=fa,subt[now]=1;
int maxson=0;
for(re int i=head[now];i;i=e[i].nxt)
{
if(e[i].t==fa) continue;
dfs1(e[i].t,now);
subt[now]+=subt[e[i].t];
if(subt[e[i].t]>maxson) maxson=subt[e[i].t],son[now]=e[i].t;
}
}
void dfs2(int now,int topfa)
{
++cnt;
id[now]=cnt,top[now]=topfa;
if(!son[now]) return;
dfs2(son[now],topfa);
for(re int i=head[now];i;i=e[i].nxt)
if(e[i].t!=f[now] && e[i].t!=son[now]) dfs2(e[i].t,e[i].t);
}
in int lca(int x,int y)
{
while(top[x]!=top[y])
{
if(d[top[x]]<d[top[y]]) x^=y,y^=x,x^=y;
x=f[top[x]];
}
if(d[x]<d[y]) x^=y,y^=x,x^=y;
return y;
}
signed main()
{
n=read(),m=read(),root=read();
for(re int i=1;i<=n-1;++i){int u=read(),v=read();add(u,v);}
dfs1(root,0);
cnt=0;
dfs2(root,root);
for(re int i=1;i<=m;++i)
{
int x=read(),y=read();
printf("%d\n",lca(x,y));
}
return 0;
}