[国家集训队]Crash的文明世界

Description

Luogu4827
BZOJ2159

给定一棵树,对于每个点(x),求出(sumlimits_{i=1}^n dist(x, i)^k)

Solution

(x^k)有关的题目,基本都要扯到第二类斯特林数上

啥是第二类斯特林数?第二类斯特林数(ig{^x_kig})表示将(x)个元素分成(k)个非空子集的方案数,(ig{^x_kig}=ig{^{x-1}_{k-1}ig}+kig{^{x-1}_kig})
这东西和(x^k)咋扯上的关系咧?(x^k = sum_{i=1}^k ig{^k_iig} cdot A_x^i),思考这个式子的组合意义就好了。当然这个式子也可以变形成(sum_{i=1}^k ig{^k_iig} cdot i! {xchoose i})

然后变形一下原来的式子(sumlimits_{i=1}^n dist(x, i)^k = sumlimits_{i=1}^n sum_{j=1}^k ig{^k_jig} cdot j! {dist(x, i)choose i}),然后就是交换一下求和顺序,预处理前两项,树形DP最后一项即可。

Code

#include <cstdio>
#include <vector>

const int N = 5e4 + 10;
const int M = 2 * N;
const int MOD = 10007;

int n, K;
std::vector<int> g[N];
int fa[N];
int S[155][155]; // 斯特林数 
int fct[155]; // 阶乘
int u[N][155], d[N][155], ans[N]; 

void dfs1(int x, int fa) {
	d[x][0] = 1;
	for (int i : g[x]) if (i != fa) {
		dfs1(i, x);
		d[x][0] = (d[x][0] + d[i][0]) % MOD;
		for (int j = 1; j <= K; ++j) {
			d[x][j] = (d[x][j] + d[i][j-1] + d[i][j]) % MOD;
		}
	}
}

void dfs2(int x, int fa) {
	u[x][0] = n-d[x][0];
	if (fa) for (int i = 1; i <= K; ++i) {
		u[x][i] = 
			u[fa][i] + d[fa][i] - d[x][i] - d[x][i-1] + 
			u[fa][i-1] + d[fa][i-1] - d[x][i-1] - d[x][i-2]
			+ 4 * MOD;
		u[x][i] %= MOD;
	}
	for (int i : g[x]) if (i != fa) dfs2(i, x);
}

int main() {
	scanf("%d%d", &n, &K);
	for (int i = 1, x, y; i < n; ++i) {
		scanf("%d%d", &x, &y);
		g[x].push_back(y);
		g[y].push_back(x);
	}
	S[1][1] = 1;
	for (int i = 2; i <= K; ++i) {
		for (int j = 1; j <= K; ++j) {
			S[i][j] = (S[i-1][j-1] + S[i-1][j]*j) % MOD;
		}
	}
	fct[1] = 1;
	for (int i = 2; i <= K; ++i) fct[i] = i * fct[i-1] % 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] + S[K][j]*fct[j]%MOD * (u[i][j]+d[i][j])%MOD) % MOD;
		}
		printf("%d
", ans[i]);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/wyxwyx/p/lg4827.html