显然这是树链剖分和LCA的结合体
然后假装你已经会了这两个东西,或者看了:
树链剖分——杨子曰算法
谈谈最近公共祖先(LCA)——杨子曰算法
这两个东西,如果你对上面那两个玩意有印象的话,黑喂狗:
树链剖分求LCA思路非常的简单,也非常好理解:
1.按重儿子剖分这棵树
这时对于同一条链上的两个点直接输出高的那个就行了,那如果两个点不在同一条链上呢?——我们又要开始跳了
2.只要两个点不在同一条链上,就重复3,否则进行4:
3.将x,y中所在链的链顶较低的点,跳到这条链的链顶的父亲
4.现在x,y在一条链上了,输出较高的点,完事!
很多大佬一定都已经明白了,我们再来模拟一下,我去我树链剖分那篇文章里偷个图:
假设我们现在要求x和y的LCA:
发现它们不在同一条链上,于是我们比较绿色链链顶和蓝色链链顶的深度,于是我们发现绿色链的链顶较低把x调到绿色链链顶的父亲1
又发现x,y还是不在同一条链上,我们又来比较红色链链顶和蓝色链链顶,我们把y调到蓝色链链顶的父亲2
现在x和y在同一条链上了!输出较高的y处在的位置结点2——就是它们的LCA
树链剖分法求LCA的优点:
1.代码不是特别的长
2.非常好理解
3.空间比倍增小
OK,完事
c++模板(洛谷P3379):
#include
using namespace std;
const int maxn=500005;
struct Edge{
int next,to;
}edge[maxn*2];
int nedge=0;
int head[maxn],f[maxn],d[maxn],son[maxn],sz[maxn],tp[maxn];
void addedge(int a,int b){
edge[nedge].to=b;
edge[nedge].next=head[a];
head[a]=nedge++;
}
void dfs1(int x,int fa,int dep){
f[x]=fa;
d[x]=dep;
sz[x]=1;
for (int i=head[x];i!=-1;i=edge[i].next){
int v=edge[i].to;
if (v==fa) continue;
dfs1(v,x,dep+1);
sz[x]+=sz[v];
if (!son[x] || sz[son[x]]<sz[v]) son[x]=v;
}
}
void dfs2(int x,int fa,int top){
tp[x]=top;
if (son[x]) dfs2(son[x],x,top);
for (int i=head[x];i!=-1;i=edge[i].next){
int v=edge[i].to;
if (v==fa || v==son[x]) continue;
dfs2(v,x,v);
}
}
int lca(int x,int y){
while(tp[x]!=tp[y]){
int f1=tp[x],f2=tp[y];
if (d[f1]>d[f2]) x=f[f1];
else y=f[f2];
}
if (d[x]>d[y]) return y;
else return x;
}
int main(){
memset(head,-1,sizeof(head));
int n,m,r;
scanf("%d%d%d",&n,&m,&r);
for (int i=1;i<n;i++){
int a,b;
scanf("%d%d",&a,&b);
addedge(a,b);
addedge(b,a);
}
dfs1(r,0,1);
dfs2(r,0,r);
while(m--){
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",lca(x,y));
}
return 0;
}
于XJ机房607