1. 程式人生 > 實用技巧 >題解 Crash 的文明世界

題解 Crash 的文明世界

題目傳送門

題目大意

給出一個\(n\)個點的樹,和常數\(k\),對於\(\forall i\in[1,n]\),求出:

\[\sum_{j=1}^{n} \text{dist}(i,j)^k \]

\(n\le 5\times 10^4,k\le 150\)

思路

真的很妙,一開始完全沒有思路,看了\(\texttt{y2823774827y}\)的題解之後瞬間懂了。

我們考慮對於\(i\)如何計算答案,我們發現這個指數非常不好看,於是我們可以使用第二類斯特林數展開,就跟組合數問題差不多的,變為:

\[\sum_{j=1}^{n}\sum_{d=0}^{\text{dist}(i,j)}\binom{\text{dist}(i,j)}{d}\begin{Bmatrix}k\\d\end{Bmatrix}d! \]

交換求和順序可以得到:

\[=\sum_{d=0}^{\min(n,k)}\begin{Bmatrix}k\\d\end{Bmatrix}d!\sum_{j=1}^{n}\binom{\text{dist}(i,j)}{d} \]

於是,我們的問題就是如何快速求出後面那個\(\sum\)。我們想到這個東西可以拆成:

\[\binom{\text{dist}(i,j)}{d}=\binom{\text{dist}(i,j)-1}{d}+\binom{\text{dist}(i,j)-1}{d-1} \]

於是,我們用換根\(dp\)解決這個問題了。具體見程式碼。

\(\texttt{Code}\)

#include <bits/stdc++.h>
using namespace std;

#define Int register int
#define MAXN 50005
#define mod 10007
#define MAXM 155

int qkpow (int a,int b){
	int res = 1;for (;b;b >>= 1,a = 1ll * a * a % mod) if (b & 1) res = 1ll * res * a % mod;
	return res;
}

int mul (int a,int b){return 1ll * a * b % mod;}
int dec (int a,int b){return a >= b ? a - b : a + mod - b;}
int add (int a,int b){return a + b >= mod ? a + b - mod : a + b;}

struct edge{
	int v,nxt; 
}e[MAXN << 1];

int top = 1,head[MAXN];

void Add_Edge (int u,int v){
	e[++ top] = edge {v,head[u]},head[u] = top;
	e[++ top] = edge {u,head[v]},head[v] = top;
} 

int n,k,S[MAXM][MAXM],fac[MAXM],dp1[MAXN][MAXM],dp2[MAXN][MAXM],tmp[MAXM];
//dp1[u][k]表示的是\sum_{j在i的子樹內(包括i)} \binom{dist(i,j)}{k} 
//dp2[u][k]表示的是\sum_{j=1}^{n} \binom{dist(i,j)}{k}

void dfs1 (int u,int fa){
	dp1[u][0] = 1;
	for (Int i = head[u];i;i = e[i].nxt){
		int v = e[i].v;
		if (v == fa) continue;
		dfs1 (v,u);
		for (Int j = 1;j <= k;++ j) dp1[u][j] = add (dp1[u][j],add (dp1[v][j],dp1[v][j - 1]));
		dp1[u][0] = add (dp1[u][0],dp1[v][0]);
	}
}

void dfs2 (int u,int fa){//換根dp 
	for (Int i = 0;i <= k;++ i) dp2[u][i] = dp1[u][i];
	if (fa){
		for (Int i = 1;i <= k;++ i) tmp[i] = dec (dp2[fa][i],add (dp1[u][i],dp1[u][i - 1]));
		tmp[0] = dec (dp2[fa][0],dp1[u][0]);
		for (Int i = 1;i <= k;++ i) dp2[u][i] = add (dp2[u][i],add (tmp[i],tmp[i - 1]));
		dp2[u][0] = add (dp2[u][0],tmp[0]);
	} 
	for (Int i = head[u];i;i = e[i].nxt){
		int v = e[i].v;
		if (v == fa) continue;
		dfs2 (v,u);
	}
}

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

signed main(){
	read (n,k),S[0][0] = fac[0] = 1;
	for (Int i = 1;i <= k;fac[i] = mul (i,fac[i - 1]),++ i)
		for (Int j = 1;j <= i;++ j)
			S[i][j] = add (S[i - 1][j - 1],mul (j,S[i - 1][j]));
	for (Int i = 2,u,v;i <= n;++ i) read (u,v),Add_Edge (u,v);
	dfs1 (1,0),dfs2 (1,0);
	for (Int i = 1;i <= n;++ i){
		int sum = 0;
		for (Int j = 0;j <= k;++ j) sum = add (sum,mul (fac[j],mul (S[k][j],dp2[i][j])));
		write (sum),putchar ('\n');
	}
	return 0;
}