LCA

最近公共祖先

1.暴力

将点按照父子关系依次向上查询,知道两个点发生重合

# include <stdio.h>
# include <string.h>
# define N  40010
# define M 2*N
using namespace std;
int Next[M],head[N],ver[M],edge[M];
int depth[N],father[M],D[M];
int tot=-1,n,m;
void ADD(int x,int y,int z)
{
    ver[++tot]=y;
    edge[tot]=z;
    Next[tot]=head[x];
    head[x]=tot;
}
int LCA(int x,int y)
{
    while(x!=y)
    {
        if(depth[x]>=depth[y])x=father[x];
        else y=father[y];
    }
    return x;
}
void dfs(int x,int fa)
{
    int y,z;
    father[x]=fa;
    depth[x]=depth[fa]+1;
    for(int i=head[x]; ~i; i=Next[i])
    {
        y=ver[i];
        z=edge[i];
        if(y==fa)continue;
        D[y]=D[x]+z;
        dfs(y,x);//查找儿子节点
    }//此dfs是为了求得depth[]数组
}
int main()
{
    int  T,x,y,z;
    register int i;
    scanf("%d%d",&n,&m);
    memset(head,0xff,sizeof(head));
    tot=-1;
    for(int i=1; i<n; i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        ADD(x,y,z);
        ADD(y,x,z);//双向边
    }
    dfs(1,-1);
    while(m--)
    {
        scanf("%d%d",&x,&y);
        printf("%d\n",D[x]+D[y]-2*D[LCA(x,y)]);
    }
    return 0;
}

2.树上倍增

在进行 dfs 的时候处理出每个节点的父子关系,在查询时以 \(2^i\) 为单位向上倍增,先将两个节点的深度调整一致,然后将两个点同时向上查询,最后两点重合时,该点即为两个点的LCA,同时也可结合倍增的处理一些权值的问题

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<math.h>
#include<vector>
#include<queue>
#define ll long long

const ll maxn=1e5+10;
ll n,q,tot,ans1,ans2;
ll head[maxn*2],dep[maxn];
ll fa[maxn][22],minn[maxn][22],maxx[maxn][22];
struct node
{
	ll u,v,w,nxt;
} s[maxn*2];

inline void add(ll u,ll v,ll w)
{
	s[++tot].v=v;
	s[tot].w=w;
	s[tot].nxt=head[u];
	head[u]=tot;
}

inline void dfs(ll x,ll f)
{
	fa[x][0]=f;
	dep[x]=dep[f]+1;
	
	for(int i=head[x];i;i=s[i].nxt)
	{
		ll y=s[i].v;
		
		if(y==f) continue;
		
		minn[y][0]=s[i].w;
		maxx[y][0]=s[i].w;
		
		dfs(y,x);
	}
}

inline void lca(ll x,ll y)
{
	if(dep[x]<dep[y]) std::swap(x,y);
	
	for(int i=18;i>=0;i--)
	{
		if(dep[fa[x][i]]>=dep[y])
		{
			ans1=std::max(ans1,maxx[x][i]);
			ans2=std::min(ans2,minn[x][i]);
			x=fa[x][i];
		}
	}
	
	if(x==y) return ;
	
	for(int i=18;i>=0;i--)
	{
		if(fa[x][i]!=fa[y][i])
		{
			ans1=std::max(ans1,std::max(maxx[x][i],maxx[y][i]));
			ans2=std::min(ans2,std::min(minn[x][i],minn[y][i]));
			
			x=fa[x][i];
			y=fa[y][i];
		}
	}
	
	ans1=std::max(ans1,std::max(maxx[x][0],maxx[y][0]));
	ans2=std::min(ans2,std::min(minn[x][0],minn[y][0]));
}

int main(void)
{
	scanf("%lld",&n);
	
	memset(minn,0x3f,sizeof(minn));
	
	for(int i=1;i<=n-1;i++)
	{
		ll x,y,z;
		scanf("%lld %lld %lld",&x,&y,&z);
		add(x,y,z);
		add(y,x,z);
	}
	
	dfs(1,0);
	
	for(int j=1;j<=18;j++)
	{
		for(int i=1;i<=n;i++)
		{
			fa[i][j]=fa[fa[i][j-1]][j-1];
			minn[i][j]=std::min(minn[i][j-1],minn[fa[i][j-1]][j-1]);
			maxx[i][j]=std::max(maxx[i][j-1],maxx[fa[i][j-1]][j-1]);
		}
	}
	
	scanf("%lld",&q);
	
	for(int i=1;i<=q;i++)
	{
		ll x,y;
		ans1=-maxn*maxn*maxn;
		ans2=maxn*maxn*maxn;
		scanf("%lld %lld",&x,&y);
		
		lca(x,y);
		
		printf("%lld %lld\n",ans2,ans1);
	}
	
	return 0; 
}
posted @ 2020-12-04 11:25  雾隐  阅读(115)  评论(0编辑  收藏  举报