【树剖求LCA】树剖知识点

不太优美但是有注释的版本:

#include<cstdio>
#include<iostream>
using namespace std;
struct edge{
    int to,ne;
}e[1000005];
int n,m,s,ecnt,head[500005],dep[500005],siz[500005],son[500005],top[500005],f[500005];
void add(int x,int y)    //加边
{
    e[++ecnt].to=y;
    e[ecnt].ne=head[x];
    head[x]=ecnt;
}
void dfs1(int x)                             //构造树
{
    siz[x]=1;                                //假设当前节点仅有一个儿子
    dep[x]=dep[f[x]]+1;                      //当前节点深度=父亲节点深度+1
    for(int i=head[x];i;i=e[i].ne)          //遍历所有的子节点
    {
        int dd=e[i].to;
        if(dd==f[x])continue;               //如果是父节点,则略过
        f[dd]=x;                             //那么确定x是当前节点的父亲
        dfs1(dd);                            //向下遍历
        siz[x]+=siz[dd];                     //遍历完子树之后,加上子树的大小
        if(!son[x]||siz[son[x]]<siz[dd])    //如果x节点重儿子未确定或者重儿子的子树比当前遍历节点的子树小
            son[x]=dd;                       //更新重儿子
    }
}

void dfs2(int x,int tv)                     //求重链
{
    top[x]=tv;                               //设置x所在重链顶为tv
    if(son[x])dfs2(son[x],tv);              //如果x有重儿子,那么随着这条重链走
    for(int i=head[x];i;i=e[i].ne)            
    {
        int dd=e[i].to; 
        if(dd==f[x]||dd==son[x])continue;  //如果走到父亲或者走到重儿子(已经走过重儿子,避免重复),那么跳过
        dfs2(dd,dd);                        //开启一条新链,链顶是其本身
    }
}
int lca(int x,int y)
{
	while(top[x]!=top[y])                              //如果二者不在同一条重链上
    { 
		if(dep[top[x]] >= dep[top[y]]) x=f[top[x]];   //选择所在重链的顶的深度较大的点向上跳,目的是防止跳过LCA
		else y=f[top[y]];
	}
	return dep[x] < dep[y] ?x :y;                     //当二者在同一条重链上的时候,选择深度较浅的点即为lca
	
	
}
int main()
{
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i<n;++i)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    dfs1(s);
    dfs2(s,s);
    for(int i=1;i<=m;++i)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        printf("%d
",lca(x,y));
    }
}

  

比较优美但是没注释的版本:

#include<iostream>
#include<vector>
#include<cstdio>
#include<queue>
#include<map>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<set>
#include<cstring>
using namespace std;
typedef long long ll;
const ll INF=99999999;
const int N = 500010;

int n,m,s;

struct edge{
	int to,ne;
	
}e[N*2];

int top[N],siz[N],son[N],fa[N],dep[N];
int head[N],ecnt = 1;

void add(int x,int y)
{
	e[ecnt].to = y;
	e[ecnt].ne = head[x];
	
	head[x] = ecnt++;
}

void dfs1(int x)
{
	siz[x] = 1;
	dep[x] = dep[fa[x]] + 1;
	
	for(int i = head[x];i;i = e[i].ne){
		int t = e[i].to;
		if(t == fa[x]) continue;
		fa[t] = x;
		
		dfs1(t);
		siz[x] += siz[t];
		if(!son[x]||siz[son[x]] < siz[t])
			son[x] = t;
	}
}

void dfs2(int x,int tp)
{
	top[x] = tp;
	
	if(son[x])
		dfs2(son[x],tp);
		
	for(int i = head[x];i;i = e[i].ne){
		int t = e[i].to;
		if(t == son[x]||t == fa[x]) continue;
		
		dfs2(t,t);
	}	

}

int lca(int x,int y)
{
	while(top[x] != top[y]){
		if(dep[top[x]] >= dep[top[y]]) x = fa[top[x]];
		else y = fa[top[y]];
	}
	return dep[x] < dep[y] ?x :y;
}
int main()
{
	
	scanf("%d%d%d",&n,&m,&s);
	for(int i = 1;i < n;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		
		add(x,y);
		add(y,x);
	}
	dfs1(s);
	dfs2(s,s);
	
	for(int i = 1;i <= m;i++){
		int a,b;
		scanf("%d%d",&a,&b);
		
		printf("%d
",lca(a,b));
	}
	return 0;
}

树剖理解容易,需要注意的是题目如果给的是双向边,e数组需要开两倍于边数

  

原文地址:https://www.cnblogs.com/dudujerry/p/10192526.html