ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【图论】倍增LCA(1/4)

2021-09-18 13:35:16  阅读:177  来源: 互联网

标签:图论 dep 深度 结点 int LCA 倍增 define


倍增LCA

B站UP主

LCA问题

在一棵树中求某两个结点的最近的公共祖先(LCA)

(两个结点向上去找到去最近的公共祖先)

LCA的大致流程

第一步——修正

将两个点修正到同一深度(并不是同一位置只是同一层)

在修正的过程中可能遇到的两种情况

  1. 两个点中的其中的一个点并非是其他那个点的祖先,这属于常规,且修正完后会去跑第二步
  2. 两个点中的其中一个点是另外一个结点的祖先,因而在修正后两个点会直接汇合。(不用再去执行第二部)
  3. 结论:在跑完修正这一步后,要做一个判断,去判断两个点是否为同一点。

第二步——并行

在深度修正后开始同时向根节点进发,直到在某一个结点汇合。

如果并行只是像乌龟一样一步步往根节点去爬,那么必然会收到一份叫TL的评测机礼品。

这里的话考虑用倍增的思想去优化LCA

倍增

二进制是一种数的进制,它可以用0和1来表示所有的数字。而将二进制的1可以给拆解出来转回十进制,也就是说,可以用2的幂次方的组合来表示所有的数字。

倍增与LCA问题的结合

\(f[i][j]数组\)

规定一个数组f[i][j]代表的是i往右跑\(2^{j}\)的位置。

所以有\(f[i][j]=i+2^j\)

同时\(i+2^j=i+2^{j-1}+2^{j-1}=(i+2^{j-1})+2^{j-1}=(f[i][j-1])+2^{j-1}=f[ f[i][j-1] ][j-1]\)

即\(f[i][j]=f[f[i][j-1]][j-1]\)

意思就是将i往右跑\(2^{j}\)的做法平摊成两次。

(这是一个递推式,或者可以说是简单的树形dp?我们只需要知道每个结点的直接父节点,我们就可以递推出所有的上几层的结点)

f[i][j]=0的0代表的是跳不到这个位置,而在lca里这点就足够的。(在跳的过程只要不超过就行,即跳过去的深度是不小于另一个比较高的点))

试跳法

  • ?借鉴了试除法去了这个名字,其实本质就是在二进制的状态下去还原这深度差

在两个点修正到同一深度后,从大到小枚举2的幂次方,如果当前能往上跳这个数字的层数,那就先跳先上去,否则继续向下枚举,当然了,如果在枚举的过程中,出现汇合的情况那么就跳出循环,返回答案。

其他

之所以有深度这些信息,是要构建出一棵树吧,而深度这个信息是相对根节点而言的,因而我们也是要去确定根节点是哪一个点,同时要利用好这个根节点去跑出所有结点的信息。

而结点和结点是通过线段来连接的,因而我们可以考虑利用一些建图方式去记录下来,比如链式前向星。

同时在跑dfs或者bfs之前开一个deep数组用来记录每一个结点的深度。

题目集

Acwing.1172 祖孙询问

image

#include <bits/stdc++.h>
#define MEM(a,x) memset(a,x,sizeof(a))
#define W(a) while(a)
#define gcd(a,b) __gcd(a,b)
#define pi acos(-1.0)
#define PII pair<int,int>
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define ll long long
#define ull unsigned long long
#define rep(i,x,n) for(int i=x;i<n;i++)
#define repd(i,x,n) for(int i=x;i<=n;i++)
#define MAX 1000005
#define MOD 1000000007
#define INF 0x3f3f3f3f
#define lowbit(x) (x&-x)
using namespace std;
const int N = 4E4+100;
int root;
int h[N],ne[N*2],e[N*2],n,idx;
void add(int a,int b)
{
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int dep[N],fa[N][25];

void bfs()
{
	queue<int> q;
	q.push(root);
	dep[root]=1;
	while(q.size())
	{
		int t=q.front();
		q.pop();
		for(int i=h[t];~i;i=ne[i])
		{
			int j=e[i];
			if(dep[j])continue;
			dep[j]=dep[t]+1;
			fa[j][0]=t;
			q.push(j);
			for(int k=1;k<=20;k++)
			{
				fa[j][k]=fa[fa[j][k-1]][k-1];
			}
				
		}
	}
}

int lca(int x,int y)
{
	int defa=1;
	/*********修正*********/
	if(dep[x]>dep[y])//使得x永远在上面 ,x的深度最小 
	{
		swap(x,y);
		defa=2;
	}
	for(int i=20;i>=0;i--)
	{
		if(dep[ fa[y][i] ]>=dep[x])
		   y=fa[y][i];
	}
	if(x==y) return defa;
	else return 0;
}
int main()
{
	memset(h,-1,sizeof(h));
	memset(dep,0,sizeof(dep));
	memset(fa,0,sizeof(fa));
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		if(b==-1)
		  root=a;
		else 
		  add(a,b),add(b,a);
	}
	bfs();
	int q;
	scanf("%d",&q);
	while(q--)
	{
		int x,y;
	    scanf("%d%d",&x,&y);
		printf("%d\n",lca(x,y));
	} 
	
    return 0;
}

标签:图论,dep,深度,结点,int,LCA,倍增,define
来源: https://www.cnblogs.com/BeautifulWater/p/15308317.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有