终于填坑了呢
长链剖分类似于每个点选最深的儿子作为重儿子的重链剖分。
树边长度均为
1
1
1的长链剖分有一些优美的性质。
可以维护以深度为下标的
D
P
DP
DP
例:Hotel加强版
有一个树形结构,每条边的长度相同,任意两个节点可以相互到达。选3个点。两两距离相等。有多少种方案?
这个题我们可以写出一个
O
(
n
2
)
O(n^2)
O(n2)的
D
P
DP
DP。
f
i
,
j
f_{i,j}
fi,j表示
i
i
i子树内与
i
i
i的距离为
j
j
j的点数。
g
i
,
j
g_{i,j}
gi,j表示
i
i
i子树内有多少对
(
a
,
b
)
(a,b)
(a,b)满足
d
e
p
a
=
d
e
p
b
dep_a = dep_b
depa=depb并且
i
i
i往上(往祖先)走
j
j
j步之后的点
x
x
x满足
(
a
,
b
,
x
)
(a,b,x)
(a,b,x)两两距离相等。
f
i
,
j
=
∑
v
∈
s
o
n
i
f
v
,
j
−
1
,
f
i
,
0
=
1
f_{i,j} = \sum_{v \in son_i} f_{v,j-1} , f_{i,0} = 1
fi,j=∑v∈sonifv,j−1,fi,0=1
g
i
,
j
=
∑
v
∈
s
o
n
i
g
v
,
j
+
1
+
∑
u
,
v
∈
s
o
n
i
f
u
,
j
−
1
f
v
,
j
−
1
g_{i,j} = \sum_{v \in son_i} g_{v,j+1} + \sum_{u,v\in son_i} f_{u,j-1}f_{v,j-1}
gi,j=∑v∈sonigv,j+1+∑u,v∈sonifu,j−1fv,j−1
这个可以用长链剖分优化到
O
(
n
)
O(n)
O(n)
首先解决空间问题,我们需要在树上重复利用
d
p
dp
dp数组。
发现长链剖分后,每个点的数组第二维大小为其重儿子
+
1
+1
+1。
那么对于
f
i
,
j
f_{i,j}
fi,j,我们完全可以让
i
i
i继承
i
i
i的重儿子
v
v
v的
f
v
,
j
f_{v,j}
fv,j,然后再把轻儿子的
f
f
f并入计算转移。
只需要将
f
i
,
j
=
f
v
,
j
+
1
,
f
i
,
0
=
1
f_{i,j} = f_{v,j+1} , f_{i,0} = 1
fi,j=fv,j+1,fi,0=1,第一个操作可以通过关于内存指针,分配地址的操作做到
O
(
1
)
O(1)
O(1),就是简单的*
f
i
=
f
v
−
1
f_{i} = f_{v} - 1
fi=fv−1,只是
f
v
f_{v}
fv会在之后的
D
P
DP
DP中被破坏,也就是说这种处理方式不支持可持久化。
对于
g
g
g,需要
g
i
,
j
=
g
v
,
j
−
1
g_{i,j} = g_{v,j-1}
gi,j=gv,j−1,这个也可以用地址分配做到
O
(
1
)
O(1)
O(1),但是需要开两倍空间。
关于轻儿子 v v v合并到 i i i,可以做到 O ( v 子 树 中 的 最 大 深 度 − d e p i ) O(v子树中的最大深度 - dep_i) O(v子树中的最大深度−depi),接下来我们可以通过证明 ∑ v 是 f a v 的 轻 儿 子 v 子 树 中 的 最 大 深 度 − d e p v = O ( n ) \sum_{v是fa_v的轻儿子} v子树中的最大深度 - dep_{v} = O(n) ∑v是fav的轻儿子v子树中的最大深度−depv=O(n)来证明我们算法的时间复杂度为 O ( n ) O(n) O(n),至于证明,一条长链的链头 v v v的 v 子 树 中 的 最 大 深 度 − d e p v = v子树中的最大深度 - dep_v= v子树中的最大深度−depv=长链的大小,所以总和就是总点数 O ( n ) O(n) O(n)
A C C o d e \rm{AC\ Code} AC Code
#include<bits/stdc++.h>
#define maxn 100005
#define LL long long
using namespace std;
int n,len[maxn],info[maxn],son[maxn],Prev[maxn<<1],to[maxn<<1],cnt_e=0;
void Node(int u,int v){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v; }
LL tf[maxn],tg[maxn<<1],*f[maxn]={tf},*g[maxn]={tg},ans;
void dfs1(int u,int ff){
for(int i=info[u],v;i;i=Prev[i])
if((v=to[i])!=ff){
dfs1(v,u);
if(len[son[u]] < len[v]) son[u] = v;
}
len[u] = len[son[u]] + 1;
}
void dfs2(int u,int ff){
if(son[u]) f[son[u]] = f[u] + 1 , g[son[u]] = g[u] - 1 , dfs2(son[u],u);
f[u][0] = 1,ans += g[u][0];
for(int i=info[u],v;i;i=Prev[i]){
if((v=to[i])!=ff && v!=son[u]){
f[v] = f[0] , f[0] += len[v];
g[v] = g[0] + len[v] - 1 , g[0] += 2 * len[v] - 1;
dfs2(v,u);
for(int j=0;j<len[v];j++)
ans += f[v][j] * g[u][j+1] + (j>0 ? g[v][j] * f[u][j-1] : 0);
for(int j=0;j<len[v];j++){
g[u][j+1] += f[u][j+1] * f[v][j];
f[u][j+1] += f[v][j];
if(j) g[u][j-1] += g[v][j];
}
}
}
}
int main(){
scanf("%d",&n);
for(int i=1,u,v;i<n;i++){
scanf("%d%d",&u,&v);
Node(u,v),Node(v,u);
}
dfs1(1,0);
f[1] = f[0] , f[0] += len[1];
g[1] = g[0] + len[1] - 1 , g[0] += 2 * len[1] - 1;
dfs2(1,0);
printf("%lld\n",ans);
}