长链剖分
长链剖分
定义:
长链剖分主要用于解决两点之间链的问题。
实现:
思路与重链剖分相同,但是选择重儿子时选择子树深度最大的儿子。
代码:
void dfs1(int x,int fa){
d[x]=d[fa]+1;
f[x]=fa;
for(int i=head[x];i;i=nxt[i]){
int y=ver[i];
if(y==fa) continue;
dfs1(y,x);
if(dep[y]>dep[son[x]]) son[x]=y;
}
dep[x]=dep[son[x]]+1;//子树中的最大深度
}
void dfs2(int x,int topfather){
top[x]=topfather;
if(son[x]) dfs2(son[x],topfather);
for(int i=head[x];i;i=nxt[i]){
int y=ver[i];
if(y==f[x]||y==son[x]) continue;
dfs2(y,y);
}
}
当然,题目不同时,\(dfs2\) 会有所不同,具体情况具体分析。
性质:
- 所有链的长度和是 \(O(n)\) 级别的。
- 任意一个点的 \(k\) 次祖先 \(y\) 所在的长链的长度大于等于 \(k\)。
- 任意一个点向上跳重链次数不超过 \(\sqrt{n}\) 次。
作用:
优化dp:快速合并以深度为下标的子树信息
例题:P5904 [POI2014]HOT-Hotels 加强版
我们可以根据题意设置一个 \(dp\) 转移,时间复杂度为 \(O(n^2)\)
所以,设 \(f[i][j]\) 表示以 \(i\) 为根的子树中,距离当前点为 \(j\) 的点数。
\(g[i][j]\) 表示以i为根的子树中,两个点到 \(LCA\) 的距离为 \(d\) ,并且他们的 \(LCA\) 到 \(i\) 的距离为 \(d−j\) 的点对数。
考虑转移:
ans+=g[i][0];
ans+=g[i][j]*f[son[i]][j-1];
f[i][j]+=f[son[i]][j-1];
g[i][j]+=g[son[i]][j+1];
我们需要运用指针来将数组的空间减少,因此在转移中可以这样:
int *f[N];
.....
f[i]=f[son[i]]-1;
g[i]=g[son[i]]-1;
整棵树是链,时间复杂度将为 \(O(n)\) 。我们推广到树:
进行长链剖分,直接从重儿子转移 \(O(1)\) ,从轻儿子转移 \(O(\sum len)\)
因此总复杂度就是 \(O(n)\)
其余状态转移暴力即可。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=4e5+5;
int n;
int d[N],dep[N],son[N];
ll *f[N],*g[N],p[N<<2],*o=p,ans;
int nxt[N],ver[N],tot,head[N];
void add(int x,int y){
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void dfs1(int x,int fa){
d[x]=d[fa]+1;
for(int i=head[x];i;i=nxt[i]){
int y=ver[i];
if(y==fa) continue;
dfs1(y,x);
if(dep[y]>dep[son[x]]) son[x]=y;
}
dep[x]=dep[son[x]]+1;
}
/*
设f[i][j]表示x在i的子树中,且d(x,i)=j的x的个数
g[i][j]为满足x,y在i的子树中且d(lca(x,y),x)=d(lca(x,y),y)=d(lca(x,y),i)+j
的数对(x,y)的个数
递推公式:
ans=g[i][0]=sum f[x][j-1]*g[y][j+1](x,y属于son[i])
g[i][j]=sum g[x][j+1](x属于son[i])
f[i][j]=sum f[x][j-1](x属于son[i])
*/
void dfs2(int x,int fa){
if(son[x]){
f[son[x]]=f[x]+1;
g[son[x]]=g[x]-1;
dfs2(son[x],x);
}
f[x][0]=1,ans+=g[x][0];
for(int i=head[x];i;i=nxt[i]){
int y=ver[i];
if(y==fa||y==son[x]) continue;
f[y]=o,o+=dep[y]<<1;
g[y]=o,o+=dep[y]<<1;
dfs2(y,x);
for(int i=0;i<dep[y];i++){
if(i) ans+=f[x][i-1]*g[y][i];
ans+=g[x][i+1]*f[y][i];
}
for(int i=0;i<dep[y];i++){
g[x][i+1]+=f[x][i+1]*f[y][i];
if(i) g[x][i-1]+=g[y][i];
f[x][i+1]+=f[y][i];
}
}
}
int main()
{
cin>>n;
for(int i=1,x,y;i<n;i++){
scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
dfs1(1,0);
f[1]=o;o+=dep[1]<<1;
g[1]=o,o+=dep[1]<<1;
dfs2(1,0);
cout<<ans<<endl;
system("pause");
return 0;
}
不关注的有难了😠😠😠https://b23.tv/hoXKV9