题解 SP7363 TREESUM-Tree Sum/[国家集训队] Crash 的文明世界
Update on 2022.2.12:才发现这题就是[国家集训队] Crash 的文明世界,所以建议直接上洛谷做(似乎可以直接点分治??)。
题意描述:
给定一棵 \(n\) 个点的树,对于每个点求出以它为根时的 \(\sum\limits_{i=1}^{n}{dep_i}^k\)
\(n\leq 50000,k\leq 20\)
看到 对于每个点
,马上想到换根 \(dp\) ,先以 \(1\) 为根求出 \(dep_u\),设 \(dp_u\) 为以 \(u\) 为根的答案,则 $$dp_u=\sum\limits_{v\in Tree(u)}(dep_v-dep_u)^k+\sum\limits_{v\notin Tree(u)}(dep_u-dep_v)^k$$
先考虑计算前一部分:
看到 \(^k\) 想到二项式定理或第二类斯特林数,先用二项式定理推推:
发现后面那个 \((-dep_u)^{k-i}\) 的系数拆不出来,无法把子树 \(\to\) 根的转移转化为儿子 \(\to\) 父亲的转移,只能放弃。
接下来用斯特林数推:
当然,由于 \(i=0\) 和 \(i>k\) 时没有意义,因此写成:
斯特林数和阶乘可以直接算,因此只要考虑 \(\binom{dep_v-dep_u}{i}\) 就可以了。
发现根从 \(u\) 转移到 \(Son_u\) 时组合数变为 \(\binom{dep_v-dep_u-1}{i}\) ,可以想到杨辉三角的结论: \(\binom{n}{m}=\binom{n-1}{m}+\binom{n-1}{m-1}\) ,于是设 \(f_{u,i}=\sum\limits_{v\in Tree(u)}\binom{dep_v-dep_u}{i}\),这样列出转移为:
换根就是把儿子的 \(f_{v,1\sim k}\) 加上父亲的贡献,每遍历到一个点计算答案就可以了。
后一部分类似。
具体地:
注意这里要从 \(k\) 到 \(1\) 修改,因为前面的修改会影响后面的修改。
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define inf 1e15
#define N 100005
#define ls k<<1
#define rs k<<1|1
#define mid ((l+r)>>1)
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define pii pair<int,int>
#define il inline
#define file(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout)
using namespace std;
il int read(){
int w=0,h=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')h=-h;ch=getchar();}
while(ch>='0'&&ch<='9'){w=w*10+ch-'0';ch=getchar();}
return w*h;
}
const int mod=1e9+7;
struct Edge{
int next,to;
}edge[N<<1];
int T,n,K;
int head[N],num;
int S[N][25];
int Dep[N],Fac[N],F[N][25],Dp[N];
void Clear(){
num=0;
for(int i=1;i<=n;i++){
Dp[i]=0;
Dep[i]=0;
head[i]=0;
for(int j=0;j<=K;j++)
F[i][j]=0;
}
}
void add(int u,int v){
edge[++num].next=head[u];
edge[num].to=v;head[u]=num;
}
void Stirling(){
Fac[0]=S[0][0]=1;
for(int i=1;i<=K;i++){
Fac[i]=Fac[i-1]*i%mod;
for(int j=1;j<=K;j++)
S[i][j]=(S[i-1][j-1]+j*S[i-1][j]%mod)%mod;
}
}
void dfs1(int u,int fa){
Dep[u]=Dep[fa]+1;
F[u][0]=F[u][1]=1;
for(int i=head[u];i;i=edge[i].next){
int v=edge[i].to;
if(v==fa)continue;
dfs1(v,u);
F[u][0]=(F[u][0]+F[v][0])%mod;
for(int j=1;j<=K;j++)
F[u][j]=(F[u][j]+F[v][j]+F[v][j-1])%mod;
}
}
void dfs2(int u,int fa){
for(int i=1;i<=K;i++)Dp[u]=(Dp[u]+S[K][i]*F[u][i]%mod*Fac[i]%mod)%mod;
for(int i=head[u];i;i=edge[i].next){
int v=edge[i].to;
if(v==fa)continue;
for(int j=K;j>=2;j--)
F[v][j]=(F[u][j]+F[u][j-1]-(F[v][j-1]<<1)%mod+mod-F[v][j-2]+mod)%mod;
F[v][1]=(F[u][1]+F[u][0]-(F[v][0]<<1)%mod+mod)%mod;
F[v][0]=F[u][0];
dfs2(v,u);
}
}
signed main(){
T=read();
K=20;
Stirling();
while(T--){
n=read();K=read();
Clear();
for(int i=2;i<=n;i++){
int u=read()+1,v=read()+1;
add(u,v);add(v,u);
}
dfs1(1,0);
dfs2(1,1);
for(int i=1;i<=n;i++)printf("%lld\n",Dp[i]);puts("");
}
return 0;
}