求解树上LCA(最近公共祖先)
11,朴素dfs(形象的说就是:爬!)
思路:
- 从两个点开始,先把深度更深的节点爬到两个节点相同的地步
- 两个节点一起上移,直到同一深度
- 过于直白,过程略
22,在线:倍增跃迁
规定: d e p [ 0 ] = 0 dep[0]=0 dep[0]=0(哨兵),默认lca第一个参的 d e p [ x ] dep[x] dep[x] ≧ \geqq ≧ d e p [ y ] dep[y] dep[y]否则交换
预处理:
- 树的深度,log表,任意节点向上跳2的幂次方步的终点
- 设 f a [ x ] [ i ] fa[x][i] fa[x][i] f a [ x ] [ i ] fa[x][i] fa[x][i] 表示节点 x x x 上跳 2 i 2^i 2i 级的节点编号
- 最关键的转移方程是:
f
a
[
x
]
[
i
]
=
f
a
[
f
a
[
x
]
[
i
−
1
]
]
[
i
−
1
]
f
a
[
x
]
[
i
]
=
f
a
[
f
a
[
x
]
[
i
−
1
]
]
[
i
−
1
]
fa[x][i]=fa[ fa[x][i-1] ][i-1]fa[x][i]=fa[fa[x][i−1]][i−1]
fa[x][i]=fa[fa[x][i−1]][i−1]fa[x][i]=fa[fa[x][i−1]][i−1]。
即: x x x 的第 i i i个祖先是 x x x 的第 2 i − 1 2^{i-1} 2i−1 个祖先的第 2 i − 1 2^{i-1} 2i−1个祖先。
注意:初始化跃迁表的时候,k从1开始!!!!
void dfs(int f,int p) 传参父节点和当前节点
{
dep[p]=dep[f]+1;
fa[p][0]=f;
for(int i=1;i<=(maxlog也可以)logn[dep[p]];i++)fa[p][i]=fa[fa[p][i-1]][i-1];
for(int i=h[p];~i;i=nxt[i])if(to[i]!=f)dfs(p,to[i]);
}
求lca:
- 1,低深度节点跳到和高深度同一深度
- 2,同一深度的节点同时上跳,直到lca的下一层
int lca(int x,int y)
{
if(dep[x]<dep[y])swap(x,y);
for(int k=maxlog;k>=0;k--)
if(dep[fa[x][k]]>=dep[y])x=fa[x][k]; 保证同深度
if(x==y)return x;
for(int k=maxlog;k>=0;k--)
if(f[x][k]!=fa[y][k])
x=f[x][k],y=fa[y][k]; 倍增树上跃迁
return fa[x][0];
}
33,tarjan:离线
使用LCA
大部分情况下我们会对倍增情有独钟,因为不光在LCA的过程中很不错,这种稀疏表的思想还能解决书上两点最大值,这一点是树剖达不到的,因为有的点间不过根
最长树边的长度
对LCA进行简单的修改
void dfs_tree(int x,int par)
{
dep[x]=dep[par]+1;
rep(i,1,9)
{
f[x][i]=f[f[x][i-1]][i-1];
g[x][i]=max(g[f[x][i-1]][i-1],g[x][i-1]);
}
for (int i=h[x];~i;i=nxt[i])
{
if(e[i]!=par)
{
f[e[i]][0]=x;
g[e[i]][0]=v[i];
dfs_tree(e[i],x);
}
}
}
double lca(int x,int y)
{
double ans= 0.0;
if(dep[x]<dep[y])swap(x,y);
for(int i=9;i>=0;i--)
{
if(dep[f[x][i]]>=dep[y])
{
ans=max(ans,g[x][i]);
x=f[x][i];
if(x==y)return ans;
}
}
for(int i=9;i>=0;i--)
{
if(f[x][i]!=f[y][i])
{
ans=max(ans,g[x][i]);
x=f[x][i];
ans=max(ans,g[y][i]);
y=f[y][i];
}
}
return max(ans,max(g[x][0],g[y][0]));
}
这里的LCA就可以返回树上(x,y)间的最长树边长了
Link 以上使用的实际例子