一、倍增思想
倍增, 从字面的上意思看就是成倍的增长 ,这是指我们在进行递推时,如果状态空间很大,通常的线性递推无法满足时间和空间复杂度的要求 ,那么我们就可以通过成倍的增长,只递推状态空间中在 2 的整数次幂位置上的值作为代表 。当需要其他位置上的值时,我们只需要通过" 任意整数可以表示成若干个2 的 次幂项的和 " 这一性质(13 = 23 + 22 +20 ), 使用之前求出的代表值拼成所需的值。
关于倍增的理解可通过此博客:【白话系列】倍增算法
二、倍增求LCA
用倍增求LCA有两部分,先是预处理,然后再求LCA。
可以先设
f
a
[
i
]
[
j
]
fa[i][j]
fa[i][j]为i号节点向上跳
2
j
2^j
2j步之后的祖先,那么有:
f
a
[
i
]
[
j
]
=
f
a
[
f
a
[
i
]
[
j
−
1
]
[
j
−
1
]
fa[i][j]=fa[fa[i][j-1][j-1]
fa[i][j]=fa[fa[i][j−1][j−1]
f
a
[
i
]
[
0
]
fa[i][0]
fa[i][0]则为i号节点的父节点,若i号节点向上走j步超出了根节点,则
f
a
[
i
]
[
j
]
=
0
fa[i][j]=0
fa[i][j]=0
求LCA(x,y)时步骤大致如下:
(1).若x节点和y节点的深度不一样,则先将深度更大的节点以2的幂次方为单位向上跳,直到x和y的深度相同为止,若x和y深度一样直接执行步骤(2).
(2).将x和y同时以2的幂次方为单位向上跳,最后使x和y的父节点相同,即
f
a
[
x
]
[
0
]
=
f
a
[
y
]
[
0
]
fa[x][0]=fa[y][0]
fa[x][0]=fa[y][0],此时
L
C
A
(
x
,
y
)
=
f
a
[
x
]
[
0
]
LCA(x,y)=fa[x][0]
LCA(x,y)=fa[x][0]
具体实现:
#include<iostream>
#include<queue>
#include<string>
using namespace std;
typedef long long ll;
const int N=5e5+10;
int dep[N],fa[N][30];//dep数组表示节点的深度
int n,m,s;
vector<int> G[N];
void dfs(int u,int father){
int i;
dep[u]=dep[father]+1;
for(int i=1;i<=20;i++)
fa[u][i]=fa[fa[u][i-1]][i-1];
for(auto to:G[u]){
if(to==father) continue;
fa[to][0]=u;//子节点往上跳一步就是自己
dfs(to,u);
}
}
int lca(int x,int y){
int i;
if(dep[x]<dep[y]) swap(x,y);//确保x比较深
for(i=20;i>=0;i--){
if(dep[fa[x][i]]>=dep[y])//找到x与y同一深度的祖先
x=fa[x][i];
if(x==y) return x; //如果y是x的祖先,则x与y的最近公共祖先是y
}
for(i=20;i>=0;i--){
if(fa[x][i]!=fa[y][i]){//枚举向上倍增
x=fa[x][i];
y=fa[y][i];
}
}
return fa[x][0];//此时x和y向上走一步即为它们的LCA
}
int main()
{
cin>>n>>m>>s;
for(int i=0;i<n-1;i++){
int a,b;
cin>>a>>b;
G[a].push_back(b);
G[b].push_back(a);
}
dfs(s,s);
while(m--){
int a,b;
cin>>a>>b;
cout<<lca(a,b)<<endl;
}
return 0;
}
例题:AcWing1171. 距离
参考代码:
#include<iostream>
#include<algorithm>
#include<queue>
#include<set>
#include<stdlib.h>
#include<time.h>
#include<unordered_map>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstring>
#include<string>
#include<map>
#include<cmath>
#include<bitset>
#define ll long long
#define inf 0x3f3f3f3f
#define bug(a) cout<<"* "<<a<<endl;
#define bugg(a,b) cout<<"* "<<a<<" "<<b<<endl;
#define buggg(a,b,c) cout<<"* "<<a<<" "<<b<<" "<<c<<endl;
using namespace std;
const int N=1e4+10;
const ll mod=1e9+7;
struct P{
int e,fa,cost;
};
struct edge{
int to,w;
};
int dep[N],fa[N][30],dis[N];
int n,m;
vector<edge> G[N];
void bfs(int u,int father){ //用bfs来进行预处理
queue<P> q;
int i;
q.push(P{u,father,0});
dep[u]=1;
dis[u]=0;
while(!q.empty()){
P f=q.front();
q.pop();
u=f.e,father=f.fa;
dep[u]=dep[father]+1;
dis[u]=dis[father]+f.cost;
for(int i=1;i<=20;i++)
fa[u][i]=fa[fa[u][i-1]][i-1];
for(auto v:G[u]){
if(v.to==father) continue;
fa[v.to][0]=u;//子节点往上跳一步就是自己
q.push(P{v.to,u,v.w});
}
}
}
int lca(int x,int y){
int i;
if(dep[x]<dep[y]) swap(x,y);
for(i=20;i>=0;i--){
if(dep[fa[x][i]]>=dep[y])
x=fa[x][i];
if(x==y) return x;
}
for(i=20;i>=0;i--){
if(fa[x][i]!=fa[y][i]){
x=fa[x][i];
y=fa[y][i];
}
}
return fa[x][0];
}
int main()
{
cin>>n>>m;
int x,y,k;
for(int i=1;i<=n-1;i++){
cin>>x>>y>>k;
G[x].push_back({y,k});
G[y].push_back({x,k});
}
bfs(1,1);
while(m--){
cin>>x>>y;
int l=lca(x,y);
cout<<dis[x]+dis[y]-dis[l]*2<<endl;
}
return 0;
}