倍增法求LCA模板初写

// 倍增法求LCA(最近公共祖先)
//可分为两步,1:预处理(Vlog(V)的时间复杂度) 
//2:查询(单步查询为log(V)的时间复杂度) 

//采用邻接表存树 
#include 
#include 
#include 
#include 
#include 
using namespace std;

const int maxn = 10005;
int anc[maxn][20],depth[maxn];  //保存祖先和深度的数组
int n,from[maxn],root = -1;    //为了求出根结点
vector <int> G[maxn];
void init()
{
  memset(anc,-1,sizeof(anc));
  memset(from,-1,sizeof(from));
  memset(depth,-1,sizeof(depth));
} 

void getData()  //获取数据 
{
	cin>>n;
	int u,v;
	for(int i = 1;i<n;i++)
	{
		cin>>u>>v;
		G[u].push_back(v);
		anc[v][0] = 0 ;   //代表v的父亲就是u
		from[v] = 1;      //排除v是根结点的可能 
	}
	for(int i = 1;i<=n;i++)
	if(from[i]==-1) root = i;  //找到根结点 	
}

void getDepth_bfs(int u)   //bfs算法求结点深度 
{
	queue<int> Q;
	Q.push(u);
	
	while(!Q.empty())     //用队列来模拟广搜,相当于层序遍历 
	{
		int v = Q.front();
		Q.pop();
		for(int i = 0;i<G[v].size();i++)
		{
			depth[G[v][i]] = depth[v]+1;
			Q.push(G[v][i]); 
		}
	} 
}

void getParents()
{
	for(int up = 1;(1<<up)<=n;++up)
	{
		for(int i = 1;i<=n;++i)
		{
			anc[i][up] = anc[anc[i][up-1]][up-1];    //代码核心 
		}
	}
	
}

int Lca(int u,int v)
{
	if(depth[u]<depth[v]) swap(u,v); //总是保持u的深度大于v,方便操作
	int i = -1,j;
	while((1<<(i+1))<=depth[u]) ++i;  //求最大二分跨度
	
	//下面这个循环是为了让u和v到同一深度
	for(int j = i;j>=0;--j)
	{
		if(depth[u]-(1<<j)>=depth[v])
		u = anc[u][j]; 
	 } 
	 
	 if(u==v) return u; //刚好是祖宗
	 
	 //再用倍增法求公共祖先
	 for(j = i;j>=0;--j)
	 {
	 	if(anc[u][j]!=anc[v][j])
	 	{
	 		u = anc[u][j];
	 		v = anc[v][j];
		 }
	  } 
	  return anc[u][0]; //上个循环只能迭代到公共祖先的子节点,所以返回这 
}


void questions()
{
	int q,u,v;
	cin>>q;
	for(int i =0;i<q;i++)
	{
		cin>>u>>v;
		int ans = Lca(u,v);
		cout<<ans<<endl;
	}
 } 
int main()
{
	init(); //初始化
	getData(); //输入数据
	depth[root] = 1; //根结点的深度为1;
	//预处理
	getDepth_bfs(root);  //通过bfs算法对每一个节点求深度
	getParents();        //求每一个结点的祖先 
	//查询 
	questions(); 
    
	return 0;	
}

你可能感兴趣的:(树,图论)