LCA回顾(主要针对倍增算法)

这个有两种方法:

一、倍增

就是维护一个节点的第\(2^0,\ 2^1,\ 2^2\ ...\)层父亲,这样的话我们在后面查询的时候就可以直接“跳”着找。

void pushup()
{
	step=log(maxd*1.0)/log(2.0);

	for(int i=1;i<=step;i++)
		for(int p=1;p<=n;p++) if( fa[p][i-1] ) fa[p][i]=fa[ fa[p][i-1] ][i-1];

	return;
}

然后查询的时候,先让两个点跳到同一深度

所以要让较深的一个点先往上跳:

if( dep[x]<dep[y] ) swap(x,y);
for(int i=step;i>=0;i--) if( dep[x]-(1<<i)>=dep[y] ) x=fa[x][i];

第二行相当于将\(dep(x)-dep(y)\)的值做二进制拆分


跳到同一深度之后,如果两个节点已经跳到同一个位置,就直接退出。

否则,就要继续同时往上跳,但是,在跳的时候要保证:它们各自跳到的位置必须是不一样的!不然的话它们可能都跳到了\(LCA\)的上面!

整个功能段见下(省略了建图部分):

struct LCA
{
	int fa[maxn][22],dep[maxn];
	int maxd;
	
	void dfs(int x,int f,int d)
	{
		fa[x][0]=f,dep[x]=d;
		maxd=max( maxd,d );
		
		for(int i=G.Head[x];i;i=G.Next[i])
		{
			Graph::Edge e=G.edges[i];
			if( e.to==f ) continue;
			
			dfs(e.to,x,d+1);
		}
		
		return;
	}
	
	int step;
	
	void pushup()
	{
		step=log(maxd*1.0)/log(2.0);
		
		for(int i=1;i<=step;i++)
			for(int p=1;p<=n;p++) if( fa[p][i-1] ) fa[p][i]=fa[ fa[p][i-1] ][i-1];
		
		return;
	}
	
	int LCA(int x,int y)
	{
		if( dep[x]<dep[y] ) swap(x,y);
		
		for(int i=step;i>=0;i--) if( dep[x]-(1<<i)>=dep[y] ) x=fa[x][i];
		if( x==y ) return y;
		
		for(int i=step;i>=0;i--)
			if( fa[x][i]!=fa[y][i] ) x=fa[x][i],y=fa[y][i];
		
		return fa[x][0];
	}
}SOL;

二、树链剖分

这个更简单,用\(top\)数组直接跳就行了……

posted @ 2019-08-01 15:38  info___tion  阅读(241)  评论(0编辑  收藏  举报