每日一题 2019/3/31

由于不会的太多了,所以开一个这个专题,每天除了刷题外要做一些自己以前一直不会的东西。

尽可能地坚持下去吧,实在是有事(比如出去打比赛了)可以断,我觉得坚持天数的大小就是我实力的上限了。

 

今天要学的是倍增LCA 。

 

题目描述:给你一颗带根树,有若干次询问,每次两个点u, v,问你两个点的最近公共祖先。

不用算法纯找的话,先求出所有点的深度,比较一下深度的,然后深的往上跳直到深度相同,再一起往上跳直到访问到同一个节点,这个节点就是最近公共祖先了。

这样询问多了肯定就T飞了。

 

接下来介绍倍增LCA:

思路也差不多,就是加入了倍增的思想,可以一次跳的更多。

首先我们要处理出来每一个点的深度,acst[i][j]表示i号点上面的第2 ^ j个祖先,把这个数组也要求出来:

void dfs(int u, int fa){
	int len = G[u].size();
	acst[u][0] = fa;
	for(int i = 1; (1 << i) <= dep[u]; i++){
		acst[u][i] = acst[acst[u][i-1]][i-1];
	}
	rep(i, 0, len - 1){
		int v = G[u][i];
		if(v != fa){
			dep[v] = dep[u] + 1;
			dfs(v, u);
		}
	}
}

接下来就是LCA的过程了,要注意的是我们是选择从大往小跳,比如让你填一个数1001001的二进制位,如果从低位往高位填是比较难的,如果不对还要回溯嘛,但是如果从高位往低位就不会了,比如先填一个1000000000,肯定是大了,直到1000000比这个数小了,就可以加进去,重复做就行了。

int LCA(int x, int y){
	if(dep[x] < dep[y]) swap(x, y);
	while(dep[x] > dep[y]) {
		x = acst[x][lg[dep[x]-dep[y]]-1];
	}
	if(x == y) return x;
	Rep(k, lg[dep[x]] - 1, 0){
		if(acst[x][k] != acst[y][k]) x = acst[x][k], y = acst[y][k];
	}
	return acst[x][0];
}

其中lg数组是预处理过的,lg[i]表示10进制i在二进制表示下的长度,预处理代码如下,稍微想一下就能理解了:

void init(){
	rep(i, 1, n){
		lg[i] = lg[i-1] + ((1 << lg[i-1]) == i);
	}
}

下面贴一下全部的代码:

是针对POJ1330的代码,所以有加一些东西

vector G[maxn];
bool vis[maxn];
int lg[maxn], dep[maxn];
int acst[maxn][22];
int n;

void dfs(int u, int fa){
	int len = G[u].size();
	acst[u][0] = fa;
	for(int i = 1; (1 << i) <= dep[u]; i++){
		acst[u][i] = acst[acst[u][i-1]][i-1];
	}
	rep(i, 0, len - 1){
		int v = G[u][i];
		if(v != fa){
			dep[v] = dep[u] + 1;
			dfs(v, u);
		}
	}
}

int LCA(int x, int y){
	if(dep[x] < dep[y]) swap(x, y);
	while(dep[x] > dep[y]) {
		x = acst[x][lg[dep[x]-dep[y]]-1];
	}
	if(x == y) return x;
	Rep(k, lg[dep[x]] - 1, 0){
		if(acst[x][k] != acst[y][k]) x = acst[x][k], y = acst[y][k];
	}
	return acst[x][0];
}

void init(){
	rep(i, 1, n){
		lg[i] = lg[i-1] + ((1 << lg[i-1]) == i);
	}
}

int main()
{
	int t; scanf("%d", &t);
	while(t--){
		scanf("%d", &n);
		init();
		fill(vis, vis + n + 1, 0);
		rep(i, 1, n) G[i].clear();
		rep(i, 1, n - 1){
			int u, v; scanf("%d %d", &u, &v);
			G[u].pb(v);
			G[v].pb(u);
			vis[v]++;
		}
		int s;
		rep(i, 1, n) if(!vis[i]) { s = i; break; }
		dep[s] = 1;
		dfs(s, -1);
		int u, v; scanf("%d %d", &u, &v);
		printf("%d\n", LCA(u, v));
	}
	return 0;
}

over

你可能感兴趣的:(每日一题)