版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_27121257/article/details/78016948
一.定义:(出自百度百科)
对于有根树T的两个结点u、v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u、v的祖先且x的深度尽可能大。
二.在线算法(同上):
以序列化的方式一个个的处理输入,也就是说在开始时并不需要已经知道所有的输入。
即在可以在原树改动的情况下随时查询
三.基本思路
位运算:
等价于
1.预处理(倍增思想)
令
为x的
的祖先
初始化:
转移方程:
{与RMQ类似:
}
inline void get_lca()
{
int k=get_k(n);// log2(n)
for(int j=1;j<=k;++j)
for(int i=1;i<=n;++i)
if(fa[i][j-1])//终点 不可省略
fa[i][j]=fa[fa[i][j-1]][j-1];
}
2.计算
{可无视}
ps:1.别用cmath函数,不是一般的慢
2.其实可以直接先用计算机试出来
,然后一直用它
inline int get_k(int x)
{
int k;
for(k=1;(1<<k)<=x;++k);
return k-1; //记得-1
}
也可打表或直接预处理出 的 值
for(int i=1;i<=n;++i)
lg[i]=lg[i-1]+(1<<(lg[i-1]+1)==i);
3.查询
inline int query(int x,int y)
{
if(deep[x]<deep[y]) swap(x,y); //默认右边深度大
int k=get_k(deep[x]);//其实也可以直接用max_k
int t=deep[x]-deep[y];
for(int i=0;(1<<i)<=t;++i)//注意限定条件
if((1<<i)&t)//利用二进制 5(101)则相应的i=0与i=2时,x才向上跳
x=fa[x][i];
if(x==y) return x;//特殊情况
for(int i=k;i>=0;--i)//依次缩小跳的幅度
if(fa[x][i]!=fa[y][i] && fa[x][i])//注意fa[x][i]的存在问题
{
x=fa[x][i];
y=fa[y][i];
}
return fa[x][0];//注意lca(x,y)为 fa[x][0]\fa[y][0]
}
PS:利用 表,可进行相应简化(跳高度){ 参考luogu的讲义 }
for(;deep[x]>deep[y];)
x=f[x][lg[deep[x]-deep[y]]];
以及本人根据lowbit而写的简化(详情见树状数组,求2进制下末位的1)
for(int t = dep[x] - dep[y]; t; t -= t & (-t))
x = fa[x][lg[t & (-t)]];
四.模板
#include <cstdio>
#include <cstdlib>
#include <cstring>
#define open(s) freopen(s".in","r",stdin); freopen(s".out","w",stdout);
#define close fclose(stdin); fclose(stdout);
using namespace std;
struct Edge
{
int to;
int next;
};
int n,m;
int cnt;
int deep[500005];
int head[500005];
int fa[500005][20]; //max: log2(n)+1 一条链(极端)
Edge edge[1000005];
inline int read()
{
int k=1;
int sum=0;
char c=getchar();
for(;'0'>c || c>'9' ;c=getchar())
if(c=='-') k=-1;
for(;'0'<=c && c<='9';c=getchar())
sum=sum*10+c-'0';
return sum*k;
}
inline void write(int x)
{
if(x<0) { putchar('-'); x*=-1; }
if(x>9) write(x/10);
putchar(x%10+'0');
}
inline void add(int x,int y)
{
++cnt;
edge[cnt].to=y;
edge[cnt].next=head[x];
head[x]=cnt;
}
inline void dfs(int pre,int p)
{
deep[p]=deep[pre]+1;
fa[p][0]=pre;
for(int i=head[p];i;i=edge[i].next)
if(edge[i].to!=pre && !deep[edge[i].to])
dfs(p,edge[i].to);
}
inline void swap(int &x,int &y)
{
int tmp=x; x=y; y=tmp;
}
inline int get_k(int x)
{
int k;
for(k=1;(1<<k)<=x;++k);
return k-1;
}
inline void get_lca()
{
int k=get_k(n);
for(int j=1;j<=k;++j)
for(int i=1;i<=n;++i)
if(fa[i][j-1])
fa[i][j]=fa[fa[i][j-1]][j-1];
}
inline int query(int x,int y)
{
if(deep[x]<deep[y]) swap(x,y);
int k=get_k(deep[x]);
int t=deep[x]-deep[y];
for(int i=0;(1<<i)<=t;++i)
if((1<<i)&t)
x=fa[x][i];
if(x==y) return x;
for(int i=k;i>=0;--i)
if(fa[x][i]!=fa[y][i] && fa[x][i])
{
x=fa[x][i];
y=fa[y][i];
}
return fa[x][0];
}
int main()
{
open("3367");
n=read();
m=read();
int x1=read();
for(int i=1;i<n;++i)
{
int x=read(),y=read();
add(x,y);
add(y,x);
}
dfs(0,x1);
get_lca();
for(int i=1;i<=m;++i)
{
int x=read(),y=read();
write(query(x,y));
putchar('\n');
}
close;
return 0;
}