【poj1330】倍增求LCA

#include<cstdio>
#include<string.h>
#include<vector>
#define N 10050
using namespace std;
int T,n,a,b,deep[N],g[N][21];
bool vis[N]; 
vector<int> p[N<<1];
void dfs(int x){
	for(int i = 0;i < p[x].size();i ++){
		if(!deep[p[x][i]]){
			deep[p[x][i]] = deep[x]+1;//兒子比爸爸深1 
			g[p[x][i]][0] = x;//從兒子向上跳2^0 步是爸爸 
			dfs(p[x][i]); 
		}
	}
}
int lca(int a,int b){
	if(deep[a] < deep[b]) swap(a,b);
	int t = deep[a] - deep[b];
	for(int i = 0;i <= 20;i ++) //取t的每一個2的n次,跳到相同  
		if((1<<i)&t) a = g[a][i];
	if(a == b) return a;
	for(int i = 20;i >= 0;i --)
		if(g[a][i] != g[b][i]){
			a = g[a][i];
			b = g[b][i];
		}
	return g[a][0];
}
int main(){
	scanf("%d",&T);
	while(T --){
		memset(g,0,sizeof g);
		memset(p,0,sizeof p);
		memset(vis,false,sizeof vis);
		memset(deep,0,sizeof deep);
		scanf("%d",&n);
		for(int i=1;i<n;i ++){
			scanf("%d%d",&a,&b);
			p[a].push_back(b);
			vis[b] = true;
		}
		scanf("%d%d",&a,&b);
		for(int i=1;i<=n;i ++){
			if(!vis[i]){//保證從根節點開始 
				dfs(i);
				break;
			}
		}	
		for(int j = 1;j <= 20;j ++)
			for(int i = 1;i <= n;i ++)
				g[i][j] = g[g[i][j-1]][j-1];
		printf("%d\n",lca(a,b));
	}
    return 0;
}

步驟分解:

  1. 用好用的vector記錄每個父親有哪些兒子,順便找到根節點——不是任何點的兒子;
  2. 從根節點開始dfs(i),每次都算出 i 的所有兒子的深度,順便寫上兒子向上跳一步是爸爸,然後dfs兒子。
  3. 遞推出g數組。
  4. 求lca。假設a深度比b大,根據深度差,跳跳跳,直到一樣深。再從大到小跳,當跳那麼多步不相同,就意味着還沒到 lca,可以一起向上跳。最後得到的a、b,是最後一次a、b不一樣,再往上跳1步就是lca了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章