Description
给定一棵有 n n n 个点的树。对于这棵树的非空节点集合 S S S,令 f ( S ) f(S) f(S) 表示包含 S S S 中所有节点的最小联通子树的边数。给定 k k k,你需要求
∑ ∅ ≠ S ⊆ [ 1 , n ] f ( S ) k \sum_{\emptyset \neq S \subseteq [1,n] }f(S)^{k} ∅=S⊆[1,n]∑f(S)k
2 ≤ n ≤ 1 0 5 , 1 ≤ k ≤ 200 2 \leq n \leq 10^{5}, 1 \leq k \leq 200 2≤n≤105,1≤k≤200
Solution
当指数比较大时,通常考虑用斯特林数的性质把 f ( S ) k f(S)^k f(S)k 变成
∑ i = 0 k ( f ( S ) i ) S ( k , i ) × i ! \sum_{i=0}^{k}{f(S) \choose i} S(k, i) \times i! i=0∑k(if(S))S(k,i)×i!
那么原式为
∑ ∅ ≠ S ⊆ [ 1 , n ] ∑ i = 0 k ( f ( S ) i ) S ( k , i ) × i ! = ∑ i = 0 k i ! × S ( k , i ) × ∑ ∅ ≠ S ⊆ [ 1 , n ] ( f ( S ) i ) \sum_{\emptyset \neq S \subseteq [1,n] } \sum_{i=0}^{k}{f(S) \choose i} S(k, i) \times i! \\ = \sum_{i=0}^{k} i! \times S(k, i) \times \sum_{\emptyset \neq S \subseteq [1,n] }{f(S) \choose i} ∅=S⊆[1,n]∑i=0∑k(if(S))S(k,i)×i!=i=0∑ki!×S(k,i)×∅=S⊆[1,n]∑(if(S))
外层可以枚举,并用递推式预处理斯特林数。考虑内层在树上的意义:对于每个非空点集的生成树,求在树中标记 i i i 条边的方案数。我们可以转换成,选一棵标记了 i i i 条边的非空点集的生成树的方案数。这个可以树上背包,设 f x , i f_{x,i} fx,i 为以 x x x 为根的子树的答案。将 x x x 的儿子一个个插入 x x x 中,假设要插入 y y y,那么分类讨论
- 答案在 x x x 的子树中。
- 答案在 y y y 的子树中,可能有一条连向 x x x 的边,也可能没有。
- 答案由 x x x 的子树和 y y y 的子树构成,可能有一条连向 x x x 的边,也可能没有。
第三点就是树上背包,枚举的范围需要和 k k k 取个 min,不然会被卡。在树形 dp 的时候,在每棵生成树上深度最浅的点也就是 x x x 统计答案,这样就不重不漏了。时间复杂度 O ( n k 2 ) O(nk^2) O(nk2)
女少口β可
Code
void dfs(int x, int fa) {
sz[x] = 1, f[x][0] = 1;
for (int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if (y != fa) {
dfs(y, x);
memeset(g, 0, sizeof(g));
for (int j = 1; j <= min(k, size[y]); j++) //case 2
g[j] = add(g[j], add(f[y][j], f[y][j - 1]));
g[0] = add(g[0], f[y][0]); //case 2
for (int j = 0; j <= min(k, size[x]); j++) //case 3
for (int k = 0; k <= min(k - j, size[y]); k++) {
int z = f[x][j] * f[y][k] % p;
g[j + k] = add(g[j + k], val);
g[j + k + 1] = add(g[j + k + 1], val);
ans[j + k] = add(ans[j + k], val);
ans[j + k + 1] = add(ans[j + k + 1], val);
}
}
for (int j = 0; j <= k; j++) f[x][j] += g[j];
}
}