天天看点

在线倍增算法求LCA

给定一棵树,询问两个节点的中点。

A[x,i]代表与x深度相差2的i次方的祖先。A满足性质

A[x,i+1]=A[A[x,i],i]

令j是使2的j次方小于等于H的最大非负整数

Ancestor(x,H)=Ancestor(A[x,j],H-2的j次方)

则LCA(x,y)=LCA(Ancestor(x,deep(y)-deep(x)),y)

#include<cstdio>
#include<cstring>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<math.h>
#include<vector>
using namespace std;


vector<int>g[10001];

int A[10001][15];
int d[10001];

void dfs(int root,int deep,int p)
{
	d[root]=deep;
	A[root][0]=p;
	for(int i=1;i<15;i++)
	{
		A[root][i]=A[A[root][i-1]][i-1];
	}
	for(int i=0;i<g[root].size();i++)
	{
		if(!d[g[root][i]])
		
			dfs(g[root][i],deep+1,root);
	}
}

void BuildTree(int N)
{
	int a,b;
	memset(A,0,sizeof(A));
	memset(d,0,sizeof(d));
	for(int i=1;i<=N;i++)g[i].clear();
	for(int i=1;i<N;i++)
	{
		scanf("%d%d",&a,&b);
		g[a].push_back(b);
		g[b].push_back(a);
	}
	dfs(1,1,0);
}

int Ancestor(int x,int len)
{
	for(int i=14;i>=0;i--)
	{
		if(len >= 1<<i)
		{
			len -= 1<<i;
			x=A[x][i];
		}
	}
	return x;
}

int LCA(int x,int y)
{
	if(d[x]>d[y])
	{
		x=Ancestor(x,d[x]-d[y]);
	}
	else
	y=Ancestor(y,d[y]-d[x]);
	if(x==y)
	return x;
	int ret;
	int i=14;
	while(i--)
	{
		if(A[x][i]==A[y][i])
		{
			ret=A[x][i];
		}
		else 
		{
			x=A[x][i];
			y=A[y][i];
		}
	}
	return ret;
}

void Answer(int x,int y)
{
	int p=LCA(x,y);
	int lenx=d[x]-d[p],leny=d[y]-d[p];
	int midlen=(lenx+leny)/2;
	int nx,ny;
	if(lenx>leny)
	{
		nx=Ancestor(x,midlen);
		if((lenx+leny)%2==0)
		{
			printf("%d\n",nx);
		}
		else
		{
			ny=A[nx][0];
			printf("%d %d\n",nx,ny);
		}
	}
	else
	{
		ny=Ancestor(y,midlen);
		if((lenx+leny)%2==0)
		{
			printf("%d\n",ny);
		}
		else 
		{
			nx=A[ny][0];
			printf("%d %d\n",nx,ny);
		}
	}
}

int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		int N;
		scanf("%d",&N);
		BuildTree(N);
		int M;
		scanf("%d",&M);
		while(M--)
		{
			int x,y;
			scanf("%d%d",&x,&y);
			Answer(x,y);
		}
		printf("\n");
	}
	return 0;
}
           
LCA