题解:SP7363 TREESUM - Tree Sum
其实和[集训队互测 2011] Crash 的文明世界是差不多的。
题意分析
树上问题
首先,\(n\) 个点 \(n-1\) 条边,显然是一个树。
设 \(\operatorname{dist}(i,j)\) 表示以 \(i\) 为根节点时,\(j\) 的深度。不难发现这其实就是任意节点为根节点时,\(i,j\) 之间的树上距离加 \(1\)。因此令 \(\operatorname{dist}(i,j)\) 表示 \(i,j\) 之间的距离。
斯特林数
对于整数 \(x\) 的 \(k\) 次方,其实可以通过第二类斯特林数展开:
因此,\(S(u)\) 就可以转化为:
树型 DP
不难发现,关键便在于对于每一个点 \(u\),求出 \(\sum\limits_{v=1}^n\dbinom{\operatorname{dist}(u,v)+1}{i}\),且 \(0\leq i\leq k\)。
如果 \(u\) 确定,其实很简单,只需要 DFS 一遍即可。但是需要对于每一个点 \(u\) 都求一次,暴力 DFS 的复杂度是 \(\mathcal O\left(n^2\right)\) 的,无法接受。
不难发现,设边 \((x,y)\),且 \(x\) 为 \(y\) 的父节点,则将树根从 \(x\) 变为 \(y\) 是会且仅会影响 \(x,y\) 的答案的。因此考虑换根 DP。
第一次 DP
设 \(\textit{dp}_{x,i}\) 表示节点 \(x\) 的子树内,\(i=i\) 时的 \(\sum\limits_{y=1}^n\dbinom{\operatorname{dist}(x,y)+1}{i}\)。
记 \(\textit{son}_x\) 表示 \(x\) 的子节点集。
显然,有:
-
\(i=0\) 时,无论 \(\operatorname{dist}(x,y)\) 取何值,总有 \(\dbinom{\operatorname{dist}(x,y)+1}{0}=1\)。因此,\(\textit{dp}_{x,0}\) 就是 \(x\) 子树内节点个数,包括自己。
-
\(i=1\) 时,\(\dbinom{\operatorname{dist}(x,x)+1}{0}=1\)。
-
\(i\neq0\) 时,考虑组合数递推公式:
\[\dbinom{\operatorname{dist}(x,y)+1}{i}=\dbinom{\operatorname{dist}(x,y)}{i}+\dbinom{\operatorname{dist}(x,y)}{i-1} \]\(\operatorname{dist}(x,y)-1\),即对应从 \(y\) 开始。因为 \(y\) 对应的是 \(y\) 的子树中的贡献,\(x\) 走进去必然经过长度为 \(1\) 的 \((x,y)\)。
故,有:
\[\textit{dp}_{x,i}=\textit{dp}_{y,i}+\textit{dp}_{y,i-1} \]
换根 DP
设根节点 \(x\) 为 \(y\) 的父节点,已知 \(\textit{dp}_{x,i}\),推出 \(y\) 为根节点时的 \(\textit{dp}_{y,i}\)。
如图:

首先,要求出 \(x\) 作为根节点 \(y\) 的子节点时的子树内的 \(\textit{dp}'_{x,i}\)。
只需要减去 \(y\) 对 \(x\) 的贡献即可。
即:
这时,想求出根节点 \(y\) 的 \(\textit{dp}'_{y,i}\),只需要加上对应的 \(\textit{dp}'_{x,i}\) 即可:
这样,我们就将根节点从 \(x\) 换成了 \(y\),这时 \(\mathcal O(k)\) 统计答案即可。
AC 代码
注意答案非负。
时间复杂度:\(\mathcal O\left(Tnk\right)\)。
//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
#define int long long
using namespace std;
constexpr const int N=20000,K=20,P=1e9+7;
int n,k,ans[N+1];
vector<int>g[N+1];
namespace base{
int qpow(int base,int n){
int ans=1;
while(n){
if(n&1){
ans=1ll*ans*base%P;
}
base=1ll*base*base%P;
n>>=1;
}
return ans;
}
int fact[K+1];
void pre(){
fact[0]=1;
for(int i=1;i<=K;i++){
fact[i]=1ll*fact[i-1]*i%P;
}
}
int C(int n,int m){
if(n<0||m<0||n<m){
return 0;
}
static int mem[N+1][K+1];
if(!m){
return 1;
}
if(mem[n][m]){
return mem[n][m];
}
return mem[n][m]=C(n-1,m)+C(n-1,m-1);
}
int Stirling2(int n,int k){
static int mem[K+1][K+1];
if(n<0||k<0||n<k){
return 0;
}
if(!k){
return !n;
}
if(mem[n][k]){
return mem[n][k];
}
return mem[n][k]=(Stirling2(n-1,k-1)+1ll*k*Stirling2(n-1,k)%P)%P;
}
}
namespace DP{
int dp[N+1][K+1];
void dfs0(int x,int fx){
dp[x][0]=dp[x][1]=1;
for(int y:g[x]){
if(y==fx){
continue;
}
dfs0(y,x);
dp[x][0]=(1ll*dp[x][0]+dp[y][0])%P;
for(int i=1;i<=k;i++){
dp[x][i]=(1ll*dp[x][i]+dp[y][i]+dp[y][i-1])%P;
}
}
}
void dfs1(int x,int fx){
ans[x]=0;
for(int i=0;i<=k;i++){
ans[x]=(1ll*ans[x]+1ll*dp[x][i]*base::Stirling2(k,i)%P*base::fact[i]%P)%P;
if(ans[x]<0){
ans[x]+=P;
}
}
for(int y:g[x]){
if(y==fx){
continue;
}
dp[x][0]=(dp[x][0]-dp[y][0])%P;
for(int i=1;i<=k;i++){
dp[x][i]=(dp[x][i]-dp[y][i]-dp[y][i-1])%P;
}
dp[y][0]=(dp[y][0]+dp[x][0])%P;
for(int i=1;i<=k;i++){
dp[y][i]=(dp[y][i]+dp[x][i]+dp[x][i-1])%P;
}
dfs1(y,x);
dp[y][0]=(dp[y][0]-dp[x][0])%P;
for(int i=1;i<=k;i++){
dp[y][i]=(dp[y][i]-dp[x][i]-dp[x][i-1])%P;
}
dp[x][0]=(dp[x][0]+dp[y][0])%P;
for(int i=1;i<=k;i++){
dp[x][i]=(dp[x][i]+dp[y][i]+dp[y][i-1])%P;
}
}
}
int main(){
memset(dp,0,sizeof(dp));
dfs0(1,0);
dfs1(1,0);
return 0;
}
}
main(){
/*freopen("test.in","r",stdin);
freopen("test.out","w",stdout);*/
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;
cin>>T;
while(T--){
for(int i=1;i<=n;i++){
g[i].resize(0);
}
cin>>n>>k;
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
u++,v++;
g[u].push_back(v);
g[v].push_back(u);
}
base::pre();
DP::main();
for(int i=1;i<=n;i++){
cout<<ans[i]<<'\n';
}
cout<<'\n';
}
cout.flush();
/*fclose(stdin);
fclose(stdout);*/
return 0;
}

浙公网安备 33010602011771号