倍增10分求调-玄关
查看原帖
倍增10分求调-玄关
920807
straysky楼主2024/5/14 23:05
#include <iostream>、
#include<cstring> 
using namespace std;
const int N=500002;
const int LM=20;
int n,m,s;
int deep[N];//每个节点在树中的高度层次 
int stjump[N][LM];//ST表-第i个点经过2的j次方跳跃能到达的具体点 
int cnt=1; 
int head[N];
int next1[N<<1];//链式前向星-建无向边-下标为边标记的扩大一倍 
int to[N<<1];
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int log2(int n){//根据树的节点数,算出树的高度 
	int ans=0;
	while((1<<ans)<=(n>>1)){//防止溢出的算法N>>1 
		ans++;
	}
	return ans;
}
void addedge(int u,int v){
	next1[cnt]=head[u];//next-边对边 
	to[cnt]=v;
	head[u]=cnt++;
}
int power=log2(n);
void dfs(int u,int f){//构建deep表和ST表 
	deep[u]=deep[f]+1;//u子节点深度= f父节点+1
	stjump[u][0]=f;//初始化st表,子节点 走一步到父节点 
	for(int p=1;p<=power;p++){//注意:构建ST表的时候,是递推-从小步--大步,跳跃的时候是从大步开始跳 
		stjump[u][p]=stjump[stjump[u][p-1]][p-1];//构建ST表-u跳8步=u跳4步到达的点再跳4步 
	} 
	for(int e=head[u];e!=0;e=next1[e]){
		if(to[e]!=f){//注意判断是否为子节点 
			dfs(to[e],u);//向下深度遍历 
		}
	} 
}
int lca(int a,int b){
	if(deep[a]<deep[b]){
		int temp=a;
		a=b;
		b=temp;//将两个点的大小深度-确定化方便统一到某个高度 -a一定深 
	}
	for(int p=power;p>=0;p--){
		if(deep[stjump[a][p]]>=deep[b]){//将最深的a点统一到b的高度,st表跳跃 
			a=stjump[a][p];
		}
	}
	if(a==b){
		return a;//特判:如果统一到一个高度后两个点相同,说明已经找到最低祖先 
	}
	for(int p=power;p>=0;p--){
		if(stjump[a][p]!=stjump[b][p]){//统一到一个高度后一起开始跳跃找公共祖先 
			a=stjump[a][p];
			b=stjump[b][p];
		}
	} 
	return stjump[a][0];//说明之前的循环,已经将两个点跳跃到公共祖先的子节点高度,最后只需要跳一步就达到了公共祖先节点 
} 
int main(int argc, char** argv) {
	memset(deep,0,sizeof(deep));
	memset(head,0,sizeof(head));
	cin>>n>>m>>s;
	int a,b; 
	for(int i=1;i<n;i++){//n-1条边,无向边 
		cin>>a>>b;
		addedge(a,b);
		addedge(b,a);
	} 
	dfs(s,s);//构建ST表-可以将初始根节点的父节点设为s,根节点不变仍然会向下遍历 
	for(int i=1;i<=m;i++){
		cin>>a>>b;
		cout<<lca(a,b)<<endl;
	}
	return 0;
}
2024/5/14 23:05
加载中...