求LCA三大模板

luogu3379为例
1、倍增算法(第三快

#include <cstdio>
#include <algorithm>
#define maxn 500005
using namespace std;
struct node
{
	int to,next;
}E[maxn*2];
int n,N,m,root,tot,lg[maxn],head[maxn],to[maxn],next[maxn],dep[maxn],fa[20][maxn];
void add_edge(int u,int v)
{
	E[++tot].to=v;
	E[tot].next=head[u];
	head[u]=tot;
}
void dfs(int u,int p)
{
	dep[u]=dep[p]+1;
	fa[0][u]=p;
	for(int i=1;(1<<i)<=dep[u];i++)
	fa[i][u]=fa[i-1][fa[i-1][u]];
	for(int v=head[u];v;v=E[v].next)
	if(p!=E[v].to)dfs(E[v].to,u);
}
int get_lca(int u,int v)
{
	if(dep[u]<dep[v])swap(u,v);
	while(dep[u]>dep[v])
	u=fa[lg[dep[u]-dep[v]]-1][u];
	if(u==v)return u;
	for(int k=lg[dep[u]]-1;k>=0;k--)
	if(fa[k][u]!=fa[k][v])
	u=fa[k][u],v=fa[k][v];
	return fa[0][u];
}
int main()
{
	scanf("%d%d%d",&n,&m,&root);
	int u,v;
	for(int i=1;i<=n-1;i++)
	{
		scanf("%d%d",&u,&v);
		add_edge(u,v);
		add_edge(v,u);
	}
	dfs(root,0);
	for(int i=1;i<=n;i++)
	lg[i]=lg[i-1]+(1<<lg[i-1]==i);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&u,&v);
		printf("%d\n",get_lca(u,v));
	}
	return 0;
}

2、tarjon算法(第二快

#include  <cstdio>
#define maxn 1000005
using namespace std;
struct node 
{
	int to,next;
}E[maxn*2];
struct noded
{
	int to,next,oppo,num;
	bool flag;
}G[maxn*2];
bool vis[maxn];
int head[maxn],m,n,root,tot,fa[maxn],list[maxn],ans[maxn];
void add_edge(int u,int v)
{
	E[++tot].to=v;
	E[tot].next=head[u];
	head[u]=tot;
}
void plus_edge(int u,int v)
{
	G[++tot].to=v;
	G[tot].next=list[u];
	list[u]=tot;
}
int find(int p)
{
	if(fa[p]==p)return p;
    return fa[p]=find(fa[p]);
}
void unionn(int a,int b)
{
	fa[find(a)]=find(b);
}
void tarjon(int u,int p)
{
	for(int i=head[u];i;i=E[i].next)
	{
		int v=E[i].to;
		if(v!=p&&!vis[v])
		{
            tarjon(v,u);
			unionn(v,u);		
			vis[v]=1;
		}
	}
	for(int i=list[u];i;i=G[i].next)
	{
		int v=G[i].to;
		if(vis[v]&&!G[i].flag)
		{
			ans[G[i].num]=find(v);
			int j=G[i].oppo;
			G[i].flag=G[j].flag=true;
		}
	}
}
int main()
{
	scanf("%d%d%d",&n,&m,&root);
	int u,v;
	for(int i=1;i<=n;i++)fa[i]=i;
	for(int i=1;i<=n-1;i++)
	{
		scanf("%d%d",&u,&v);
		add_edge(u,v);
		add_edge(v,u);
	}
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&u,&v);
		plus_edge(u,v);
		G[tot].oppo=tot+1;
		G[tot].num=i;
		plus_edge(v,u);
		G[tot].oppo=tot-1;
		G[tot].num=i;
	}
	tarjon(root,0);
	for(int i=1;i<=m;i++)
	printf("%d\n",ans[i]);
	return 0;
}

3、树链剖分(最快
过程:
首先预处理每个子树的大小和每个节点的深度,并对每个点选择子树大小最大的二字作为重儿子,然后顺着重儿子dfs,把重链顶点标记到每个点上。
接着求LCA时,就从两个点选dep[top[x]]更大的,跳到fa[top[x]],直到两个节点都在同一条重链上(即top[x]相等)
这样我们就在O(n)预处理的情况下,O(log n)地在线回答了LCA问题,并且常数因子非常小,比倍增的要小得多
代码:

#include <cstdio>
#include <algorithm>
#include <iostream>
#define maxn 500005
using namespace std;
struct node
{
	int to,next;
}E[maxn*2];
int tot,m,n,root,head[maxn],dep[maxn],son[maxn],top[maxn],size[maxn],fa[maxn];
void add_edge(int u,int v)
{
	E[++tot].to=v;
	E[tot].next=head[u];
	head[u]=tot;
}
void dfs1(int u,int p)
{
	dep[u]=dep[p]+1;
	size[u]=1;
	for(int i=head[u];i;i=E[i].next)
	{
		int v=E[i].to;
		if(v==p)continue;
		fa[v]=u;
		dfs1(v,u);
		size[u]+=size[v];
		if(size[v]>size[son[u]])son[u]=v;
	}
}
void dfs2(int u,int topp)
{
	top[u]=topp;
	if(son[u])dfs2(son[u],topp);
	for(int i=head[u];i;i=E[i].next)
	{
		int v=E[i].to;
		if(v==fa[u]||v==son[u])continue;
		dfs2(v,v);
	}
}
int get_lca(int u,int v)
{
	while(top[u]!=top[v])
	{
		if(dep[top[u]]<dep[top[v]])swap(u,v);
		u=fa[top[u]];
	}
	return dep[u]<dep[v]?u:v;
}
int main()
{
	scanf("%d%d%d",&n,&m,&root);
	int u,v;
	for(int i=1;i<=n-1;i++)
	{
		scanf("%d%d",&u,&v);
		add_edge(u,v);
		add_edge(v,u);
	}
	dfs1(root,0);
	dfs2(root,root);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&u,&v);
		printf("%d\n",get_lca(u,v));
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值