题意
传送门
给一棵树
求每个点\(S(i)=\sum\limits_{j=1}^ndist(i,j)^k\)。对10007取模。
思路
已知:\(n^k=\sum\limits_{i\ge 0}\left\{ \dfrac{k}{i} \right\} n^{\underline{i}}\)
直接推答案柿子:
\(ans_x\)
\(=\sum\limits_{i=1}^n i! \sum\limits_{j=0}^k \left\{ \dfrac{k}{j} \right\} dis(x,i)^{\underline{j}}\)
\(=\sum\limits_{i=1}^n \sum\limits_{j=0}^k \left\{ \dfrac{k}{j} \right\} \dbinom{dis(x,i)}{j} j!\)
\(=\sum\limits_{j=0}^k \left\{ \dfrac{k}{j} \right\} j! \sum\limits_{i=1}^n\dbinom{dist(x,i)}{j}\)
\(=\sum\limits_{j=0}^k \left\{ \dfrac{k}{j} \right\} j! \sum\limits_{i=1}^n(\dbinom{dist(x,i)-1}{j}+\dbinom{dist(x,i)-1}{j-1})\)
因此可以通过组合数递推,反推到树上dp递推(距离减一不就是儿子的状态吗),存状态\(dp[u][j]\)为\(u\)子树内,所有点\(i\)对\(u\)贡献\(\dbinom{dist(u,i)}{j}\)的贡献和。
递推柿很简洁:\(dp[u][j]=dp[son][j]+dp[son][j-1]\)
含义为:所有点到\(u\)贡献\(\dbinom{dist(u,i)}{j}\)可以划分为所有点到\(son\)贡献\(\dbinom{dist(son,i)}{j}+\dbinom{dist(son,i)}{j-1}\)。
因此想要把数学推理到dp上来,就要了解这个抽象的数学柿子表达的什么意思。
code
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+5;
const int M=155;
const int mod=10007;
int jc[N],S[M][M],n,K;
int dp[N][M],nxt[N<<1],to[N<<1],head[N],ecnt;
void add_edge(int u,int v) {nxt[++ecnt]=head[u];to[ecnt]=v;head[u]=ecnt;}
void dfs1(int u,int fa) {
dp[u][0]=1;
for(int i=head[u];i;i=nxt[i]) {
int v=to[i];if(v==fa)continue;
dfs1(v,u);
for(int j=1;j<=K;j++) {dp[u][j]=(dp[u][j]+dp[v][j]+dp[v][j-1])%mod;}
dp[u][0]=(dp[u][0]+dp[v][0])%mod;
}
}
int ans[N];
void dfs2(int u,int fa) {
int tmp[M];
for(int j=0;j<=K;j++) {ans[u]=(ans[u]+S[K][j]*jc[j]%mod*dp[u][j])%mod;tmp[j]=dp[u][j];}
for(int i=head[u];i;i=nxt[i]) {
int v=to[i];if(v==fa)continue;
for(int j=1;j<=K;j++) {dp[u][j]=(dp[u][j]-dp[v][j]-dp[v][j-1])%mod;}
dp[u][0]=(dp[u][0]-dp[v][0])%mod;
for(int j=1;j<=K;j++) {dp[v][j]=(dp[v][j]+dp[u][j]+dp[u][j-1])%mod;}
dp[v][0]=(dp[v][0]+dp[u][0])%mod;
dfs2(v,u);
for(int j=0;j<=K;j++) dp[u][j]=tmp[j];
}
}
void init() {
S[0][0]=1;
for(int i=1;i<=K;i++) {
S[i][0]=0;
for(int j=1;j<=i;j++) {
S[i][j]=(S[i-1][j-1]+j*S[i-1][j])%mod;
}
}
jc[0]=1;for(int i=1;i<=n;i++)jc[i]=jc[i-1]*i%mod;
}
int main() {
scanf("%d%d",&n,&K);
init();
for(int i=1;i<n;i++) {int u,v;scanf("%d%d",&u,&v);add_edge(u,v),add_edge(v,u);}
dfs1(1,0),dfs2(1,0);
for(int i=1;i<=n;i++) {printf("%d\n",(ans[i]+mod)%mod);}
return 0;
}