贴几篇LCA知识点的博客详解:
CSDN - 青烟绕指柔!的博客 - 倍增算法
CSDN - Nekroz_的博客
CSDN - Dust_Heart的博客
CSDN - creatorx的博客 -Tarjan算法
洛谷P3379 【模板】最近公共祖先(LCA)
倍增模板题
#include <iostream>
#include <algorithm>
#include <cstring>
#include <math.h>
#include <stdio.h>
using namespace std;
// LCA 最近公共祖先 倍增
const int N = 5e5+10;
int n ,m ,s;
int e[2*N], pr[2*N], h[2*N], idx = 0; // 链式前向星
int depth[N], f[N][30]; // depth[]深度 f[i][j]是i的2^j级祖先
// 加边 (两个无向边)
void add(int x, int y){
e[++idx] = y, pr[idx] = h[x], h[x] = idx;
}
// dfs得到每一点的 深度 和 所有祖先 ,根结点深度为1
void dfs(int x, int fa){
depth[x] = depth[fa]+1, f[x][0] = fa;
for(int i = 1; (1<<i) <= depth[x]; i++) f[x][i] = f[f[x][i-1]][i-1];
for(int i = h[x]; i; i = pr[i]) {
if(e[i] != fa) dfs(e[i],x);
}
}
// 计算最近公共祖先 倍增
int lca(int x, int y){
//1. 将x y跳到同一层(同一高度)
//2. 将x y按照距离从高到低跳,若祖先相同,说明此时有跳过的可能,继续看下一次;
// 若不同则跳到该层,从该层继续跳(不会跳不到,可以手动模拟一下)
if(depth[x] < depth[y]) swap(x,y);
while(depth[x] > depth[y]) x = f[x][(int)(log2(depth[x] - depth[y]))];
if(x == y) return x;
for(int i = log2(depth[x]); i >= 0; i--)
if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
return f[x][0];
}
int main(){
// n-1条无向边 m次询问 s为根节点
scanf("%d%d%d",&n,&m,&s);
for(int i = 1; i < n; i++){
int x, y;
scanf("%d%d",&x,&y);
add(x,y), add(y,x);
}
dfs(s,0);
while(m--){
int a,b;
scanf("%d%d",&a,&b);
cout<<lca(a,b)<<endl;
}
return 0;
}
POJ1330 - Nearest Common Ancestors
水题…两遍bfs也能过…贴一个lca的代码…顺便吐槽一下poj每次提交都得CE几发
倍增代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <math.h>
#include <stdio.h>
#include <queue>
using namespace std;
const int N = 1e5+10;
int e[N], pr[N], h[N], idx = 0;
int f[N][30], depth[N], in[N];
double l2(double x){
return log(x)/log(2.0);
}
void add(int x, int y){
e[++idx] = y, pr[idx] = h[x], h[x] = idx;
}
void bfs(int x, int fa){
depth[x] = depth[fa]+1, f[x][0] = fa;
for(int i = 1; i <= l2(depth[x]); i++) f[x][i] = f[f[x][i-1]][i-1];
for(int i = h[x]; i; i = pr[i]){
if(e[i] != fa) bfs(e[i], x);
}
}
int lca(int x, int y){
if(depth[x] < depth[y]) swap(x,y);
while(depth[x] > depth[y]) x = f[x][(int)l2(depth[x] - depth[y])];
if(x == y) return x;
for(int i = l2(depth[x]); i >= 0; i--){
if(f[x][i] != f[y][i]) x = f[x][i], y= f[y][i];
}
return f[x][0];
}
int main(){
int cas; scanf("%d",&cas);
while(cas--){
idx = 0;
memset(h, 0, sizeof h);
memset(in, 0 ,sizeof in);
int n; scanf("%d",&n);
for(int i = 1; i < n ;i ++){
int x , y; scanf("%d%d",&x, &y);
add(x,y); in[y]++;
}
for(int i = 1; i <= n; i++)
if(!in[i]) depth[0] = 0, bfs(i,0);
int x, y;
cin>>x>>y;
cout<< lca(x,y) <<endl;
}
return 0;
}
POJ1470 - Closest Common Ancestors
- 链接:http://poj.org/problem?id=1470
- 离线Tarjan
#include <iostream>
#include <algorithm>
#include <cstring>
#include <math.h>
#include <stdio.h>
#include <set>
#include <vector>
using namespace std;
const int N = 1e3+10;
int in[N], fa[N], vis[N], ask[N][N], n, num[N];
vector<int>g[N];
void init(){
memset(in,0,sizeof in);
memset(ask,0,sizeof ask);
memset(vis, 0, sizeof vis);
memset(num, 0 ,sizeof num);
for(int i = 1; i <= n; i++){
g[i].clear();
fa[i] = i;
}
}
int fin(int x){
if(fa[x] == x) return x;
return fa[x] = fin(fa[x]);
}
void lca(int r){
for(int i = 1; i <= n; i++)
if(vis[i] && ask[r][i]) num[fin(i)] += ask[r][i];
vis[r] = 1;
for(int i = 0; i < g[r].size(); i++){
int t = g[r][i];
lca(t);
fa[t] = r;
}
}
int main(){
while(~scanf("%d",&n)){
init();
int x, m, y;
for(int i = 1; i <= n; i++) {
scanf("%d:(%d)",&x,&m);
while(m--){
scanf("%d",&y);
g[x].push_back(y);
in[y] ++;
}
}
scanf("%d",&m);
while(m--){
scanf(" (%d %d)",&x,&y);
ask[x][y] ++, ask[y][x] ++;
}
for(int i = 1; i <= n; i++){
if(!in[i]) lca(i);
}
for(int i = 1; i <= n; i++)
if(num[i]) cout<<i<<':'<<num[i]<<endl;
}
return 0;
}