由于不会的太多了,所以开一个这个专题,每天除了刷题外要做一些自己以前一直不会的东西。
尽可能地坚持下去吧,实在是有事(比如出去打比赛了)可以断,我觉得坚持天数的大小就是我实力的上限了。
今天要学的是倍增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