[国家集训队] Crash 的文明世界
[国家集训队] Crash 的文明世界
题目大意:如同\([POI2008] STA-Station\),不过求每个点\(\sum\limits_{j = 1}^{n}dis(i,j)^k\)
Solution
太诡了
\[x^n = \sum\limits_{i=1}^{n}\begin{Bmatrix}n \\i \end{Bmatrix}\cdot A_x^i
\]
考虑一个组合意义证明,就是给\(n\)个格子染\(x\)种色的方案数,等于先把这\(n\)个格子分成若干集合,在\(x\)个颜色中选出相同个数的颜色,每个集合染一种颜色(排列)的方案数。
然后将排列数转化为可以应用帕斯卡定理的组合数
\[x^n = \sum\limits_{i=1}^{n}\begin{Bmatrix}n \\i \end{Bmatrix}\cdot i!\cdot\binom{x}{i}
\]
然后我们就将题目给出的式子转化
\[\sum\limits_{j=1}^{n}dis(i,j)^k=\sum\limits_{j=1}^{n}
\sum_{m=1}^{k}\begin{Bmatrix}k \\m \end{Bmatrix}\cdot m!\cdot\binom{dis(i,j)}{m}=\sum\limits_{m=1}^{k}\begin{Bmatrix}k \\m \end{Bmatrix}\cdot m!\sum\limits_{j=1}^{n}\binom{dis(i,j)}{m}
\]
第二类斯特林数的递推公式
将p个物体划分成k个非空的不可辨别的集合的方法数。
\[\begin{Bmatrix}n \\x \end{Bmatrix} =\begin{Bmatrix}n-1 \\x \end{Bmatrix} \cdot x + \begin{Bmatrix}n-1 \\x-1 \end{Bmatrix}
\]
树形DP
设\(d[x,i]\)表示当上面的\(k=i\)时\(x\)的子树对\(x\)的贡献,\(u[x,i]\)表示除了\(x\)子树外的节点对\(x\)的贡献。
-
\(d[x][j] = (d[x][j] + d[e[i].to][j - 1] + d[e[i].to][j])\)
-
\(u[x][m] = (u[x][m] + u[fa][m] + u[fa][m-1])\)
\(u[x][m] = (u[x][m] + d[fa][m] + d[fa][m-1])\)
$ u[x][m] = (u[x][m] - d[x][m] - d[x][m - 1]) \( \)u[x][m] = (u[x][m] - d[x][m - 1]-d[x][m - 2])$
\(u\)加上父亲结点的所有,减去的分别是\(x\)的子树对\(u[fa,m]\)和\(up[fa,m-1]\)的贡献。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 5e4 + 10;
const int M = N << 1;
const int Mod = 10007;
int n, K, ecnt;
int head[N], fac[N];
int s2[N][200], d[N][200], u[N][200], ans[N];
struct Edge{
int to, next;
}e[M];
inline void adde(int x, int y) {
e[++ecnt].to = y;
e[ecnt].next = head[x];
head[x] = ecnt;
}
inline int read() {
int w = 0, f = 1;
char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
w = (w << 3) + (w << 1) + (c ^ 48);
c = getchar();
}
return w * f;
}
void dfs1(int x, int fa) {
d[x][0] = 1;//实质上是size
for(int i = head[x]; i; i = e[i].next) {
if(e[i].to != fa) {
dfs1(e[i].to, x);
d[x][0] = (d[x][0] + d[e[i].to][0]) % Mod;//加上子节点的size
for(int j = 1; j <= K; ++j) {
d[x][j] = (d[x][j] + d[e[i].to][j - 1] + d[e[i].to][j]) % Mod;//更新子树内的
}
}
}
}
void dfs2(int x, int fa) {
u[x][0] = n - d[x][0];//所有节点减去子树内的
if(fa) {
for (int m = 1; m <= K; ++m) {
u[x][m] = (u[x][m] + u[fa][m] + u[fa][m-1]) % Mod;
u[x][m] = (u[x][m] + d[fa][m] + d[fa][m-1]) % Mod;
u[x][m] = (u[x][m] - d[x][m] - d[x][m - 1]) % Mod;
u[x][m] = (u[x][m] - d[x][m - 1]) % Mod;
if(m > 1) u[x][m] = (u[x][m] - d[x][m - 2]) % Mod;
u[x][m] = (u[x][m] + Mod) % Mod;
}
}
for(int i = head[x]; i; i = e[i].next)
if(e[i].to != fa) dfs2(e[i].to, x);
return;
}
int main() {
cin >> n >> K;
for(int i = 1, la, lb; i <= n - 1; ++i) {
la = read(), lb = read();
adde(la, lb);
adde(lb, la);
}
s2[1][1] = 1;
for(int i = 2; i <= K; ++i)
for(int j = 1; j <= K; ++j) {
s2[i][j] = ((s2[i - 1][j] * j) % Mod + s2[i - 1][j - 1]) % Mod;
}
fac[1] = 1;
for(int i = 2; i <= K; ++i){
fac[i] = (fac[i - 1] * i) % Mod;
}
dfs1(1, 0);
dfs2(1, 0);
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= K; ++j) {
ans[i] = (ans[i] + (s2[K][j] % Mod * fac[j] % Mod) % Mod * (d[i][j] + u[i][j]) % Mod) % Mod;
}
printf("%d\n", ans[i] % Mod);
}
return 0;
}