所谓长链剖分,就是对长链进行剖分
(逃)
前言
很优雅的算法
利用对指针进行魔法操作将
n
2
n^2
n2 的 dp 优化成线性
线性啊!!!
解析
给定一棵以 1 1 1 为根, n n n 个节点的树。设 d ( u , x ) d(u,x) d(u,x) 为 u u u 子树中到 u u u 距离为 x x x 的节点数。
对于每个点,求一个最小的 k k k,使得 d ( u , k ) d(u,k) d(u,k) 最大。
长剖板子题
设
f
x
,
d
f_{x,d}
fx,d 表示x子树内到x距离为d的结点个数
则有:
f
x
,
d
←
f
s
o
n
,
d
−
1
f_{x,d}\gets f_{son,d-1}
fx,d←fson,d−1
所以其实就是下标偏离了一位而已
那么如何解决呢?
长链剖分
定义一个子树的深度是子树内深度最大的儿子的深度
定义一个结点的重儿子是子树深度最大的儿子
和重链剖分类似的,重儿子连成重链,单独结点也算一个重链,每个结点都在唯一的重链上
void dfs1(int x,int fa){
for(int i=fi[x];~i;i=p[i].nxt){
int to=p[i].to;
if(to==fa) continue;
dfs1(to,x);
if(1) son[x]=to;
}
len[x]=len[son[x]]+1;
}
指针偏移
既然我们的父亲dp值只是和儿子相比偏移了一位
那么我们就尝试利用指针的便宜让父亲直接继承儿子的dp
当然,继承所有儿子的dp是无法实现的,那么我们就让父亲继承重儿子的dp值,其他儿子暴力转移
void dp(int x,int fa){
f[x][0]=1;
if(son[x]){
f[son[x]]=f[x]+1;
dp(son[x],x);
ans[x]=ans[son[x]]+1;
}
for(int i=fi[x];~i;i=p[i].nxt){
int to=p[i].to;
if(to==fa||to==son[x]) continue;
f[to]=now;now+=len[to];
dp(to,x);
for(int j=0;j<len[to];j++){
f[x][j+1]+=f[to][j];
if(f[x][j+1]>f[x][ans[x]]||(f[x][j+1]==f[x][ans[x]]&&j+1<ans[x])) ans[x]=j+1;
}
}
if(f[x][ans[x]]==1) ans[x]=0;
}
复杂度证明
每个重链从链头向父亲转移,会产生重链长度的代价
由于重链长度和为结点个数,所以复杂度为
O
(
n
)
O(n)
O(n)
注意! 和重链剖分不同,每个结点到根节点的长链个数可以达到 O ( n ) O(\sqrt n) O(n) 级别
完整代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=1e6+100;
const double eps=1e-12;
inline ll read(){
ll x(0),f(1);char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
int n;
struct node{
int to,nxt;
}p[N<<1];
int fi[N],cnt;
inline void addline(int x,int y){
p[++cnt]=(node){y,fi[x]};fi[x]=cnt;
return;
}
int son[N],len[N];
int buf[N],*f[N],*now=buf;
void dfs(int x,int f){
for(int i=fi[x];~i;i=p[i].nxt){
int to=p[i].to;
if(to==f) continue;
dfs(to,x);
if(len[to]>len[son[x]]) son[x]=to;
}
len[x]=len[son[x]]+1;
}
int ans[N];
void dp(int x,int fa){
f[x][0]=1;
if(son[x]){
f[son[x]]=f[x]+1;
dp(son[x],x);
ans[x]=ans[son[x]]+1;
}
for(int i=fi[x];~i;i=p[i].nxt){
int to=p[i].to;
if(to==fa||to==son[x]) continue;
f[to]=now;now+=len[to];
dp(to,x);
for(int j=0;j<len[to];j++){
f[x][j+1]+=f[to][j];
if(f[x][j+1]>f[x][ans[x]]||(f[x][j+1]==f[x][ans[x]]&&j+1<ans[x])) ans[x]=j+1;
}
}
if(f[x][ans[x]]==1) ans[x]=0;
}
signed main(){
#ifndef ONLINE_JUDGE
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
#endif
memset(fi,-1,sizeof(fi));cnt=-1;
n=read();
for(int i=1;i<n;i++){
int x=read(),y=read();
addline(x,y);addline(y,x);
}
dfs(1,0);
f[1]=now;now+=len[1];dp(1,0);
for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
return 0;
}
/*
*/
Thanks for reading!