[国家集训队] Crash 的文明世界
链接:https://www.luogu.com.cn/problem/P4827
题目描述:对于每一个\(i\),在一棵给定的树上求\(\sum_{j=1}^{n}dist(i,j)^k\),其中\(k<=150\)。
题解:发现\(k<=150\),我们可以考虑利用斯特林数将\(n\)转化为\(k\)。
\(\qquad\sum_{j=1}^{n}dist(i,j)^k\)
\(\quad=\sum_{j=1}^{n}\sum_{t=1}^{n}S(k,t)\times C(dist(i,j),t)\times t!\)
\(\quad=\sum_{j=1}^{n}\sum_{t=1}^{k}S(k,t)\times C(dist(i,j),t)\times t!\)
\(\quad=\sum_{t=1}^{k}t!\times S(k,t)\sum_{j=1}^{n}C(dist(i,j),t)\)
所以我们只要能快速求出\(\sum_{j=1}^{n}C(dist(i,j),t)\)就可以了,我们可以令\(dp_{i,j}\)表示第\(i\)个节点的子树中\(C(dist(i,j),t)\)的和,则有
\(dp_{i,j}=dp_{v,j-1}+dp_{v,j}\)
这样可以得到\(root\)的答案,可以换根\(dp\)得到其他节点的答案。
#include<iostream>
#define mod 10007
using namespace std;
struct node
{
int v,nxt;
};
node edge[100001];
int len,head[100001],ans,n,k,f[100001][151],dp[100001][151],S[151][151],fac[151];
bool used[100001];
void add(int x,int y)
{
edge[++len].v=y;
edge[len].nxt=head[x];
head[x]=len;
return;
}
void dfs(int x)
{
used[x]=1;
f[x][0]=1;
for (int i=head[x];i>0;i=edge[i].nxt)
if (!used[edge[i].v])
{
dfs(edge[i].v);
f[x][0]=(f[x][0]+f[edge[i].v][0])%mod;
for (int j=1;j<=k;++j)
f[x][j]=(f[x][j]+f[edge[i].v][j]+f[edge[i].v][j-1])%mod;
}
return;
}
void redfs(int x)
{
used[x]=0;
for (int i=head[x];i>0;i=edge[i].nxt)
if (used[edge[i].v])
{
dp[edge[i].v][0]=n;
dp[edge[i].v][1]=(dp[x][1]+dp[x][0]-f[edge[i].v][0]*2)%mod;
for (int j=2;j<=k;++j)
dp[edge[i].v][j]=(dp[x][j]+dp[x][j-1]-f[edge[i].v][j-1]*2-f[edge[i].v][j-2])%mod;
redfs(edge[i].v);
}
return;
}
int main()
{
int x,y;
cin>>n>>k;
for (int i=1;i<=n-1;++i)
{
cin>>x>>y;
add(x,y);
add(y,x);
}
dfs(1);
for (int i=0;i<=k;++i)
dp[1][i]=f[1][i];
redfs(1);
S[0][0]=1;
for (int i=1;i<=k;++i)
for (int j=1;j<=i;++j)
S[i][j]=(S[i-1][j-1]+S[i-1][j]*j%mod)%mod;
fac[0]=1;
for (int i=1;i<=k;++i)
fac[i]=fac[i-1]*i%mod;
for (int q=1;q<=n;++q)
{
ans=0;
for (int i=0;i<=k;++i)
ans=(ans+fac[i]*S[k][i]%mod*dp[q][i]%mod)%mod;
cout<<(ans+mod)%mod<<endl;
}
return 0;
}
本文来自博客园,作者:zhouhuanyi,转载请注明原文链接:https://www.cnblogs.com/zhouhuanyi/p/16983629.html

浙公网安备 33010602011771号