倍增学习
基本原理
- 对于一个n长的道路,若一步一步的跳,复杂度过高
- 如何优化跳的方式???
- 存在一些数1,2,4,8,16,32…
- 对于任意数 x 都能被这些数所组合,且最多取一个
- 因为如果取两个4,为啥不直接取一个8
- 倍增处理:对于道路上的每个点,求出各点跳到其右边 1 , 2 , 4 , 8 , 16 , 2 k 1,2,4,8,16,2^k 1,2,4,8,16,2k 的内容,可用 f [ i ] [ j ] f[i][j] f[i][j] 表示第 i 个点所距离 2 j 2^j 2j 的点
- 预处理方式:常有的有树上倍增和线性倍增
线性倍增
树上倍增求LCA
- lca定义:树上两个点的最近公共祖先
- 暴力方法:让两个点不停的往上跳,直到调到他们的 l c a lca lca
- 倍增原理:一步跳多格,快速跳到他们的 l c a lca lca
上核心代码及注释
void dfs(int u,int fa) {
dep[u]=dep[fa]+1;
f[u][0]=fa;
for(int i=1; i<=20; i++)f[u][i]=f[f[u][i-1]][i-1]; //树上倍增预处理 nlogn
for(int i=vex[u]; i; i=e[i].next) {
int v=e[i].v;
if(v==fa)continue;
dfs(v,u);
}
}
int lca(int x,int y) {
if(dep[x]<dep[y])swap(x,y);
for(int i=20; i>=0; i--) {
if(dep[f[x][i]]<dep[y])continue;
x=f[x][i];
//深的先跳,直到跳到与浅的深度一致
}
if(x==y)return y;//如果相同则 y就为 lca
for(int i=20; i>=0; i--) {
if(f[x][i]==f[y][i])continue;
x=f[x][i];
y=f[y][i];
//若不同,则二者一起跳,直到跳到二者均为 lca的儿子节点
}
return f[x][0];
}
最近公共祖先
用途
- 1.对于树上的一条路径 ( x , y ) (x,y) (x,y),其等价于 ( x , l c a ) + ( l c a , a ) − l c a (x,lca)+(lca,a)-lca (x,lca)+(lca,a)−lca
- 2.对于树上的一条路径 ( x , y ) (x,y) (x,y),其还可以等价于 ( r o o t , x ) + ( r o o t , y ) − ( r o o t , l c a ) − ( r o o t , f a [ l c a ] ) (root,x)+(root,y)-(root,lca)-(root,fa[lca]) (root,x)+(root,y)−(root,lca)−(root,fa[lca])
- 转化之后可以利用差分,前缀和等思路将位置转化简单。
- 例如:求两个点的距离 d i s = d e p [ x ] + d e p [ y ] − 2 × d e p [ l c a ] dis=dep[x]+dep[y]-2\times dep[lca] dis=dep[x]+dep[y]−2×dep[lca]