#第二类斯特林数,树形dp#洛谷 4827 JZOJ 1940 BZOJ 2159 Crash的文明世界

本文介绍了一种使用树形动态规划(DP)结合Stirling数解决特定问题的算法。通过双层求和公式,将节点间距离转化为Stirling数的组合,实现了对树状结构的有效遍历与计算。文章提供了详细的数学推导过程及C++实现代码,展示了如何通过换根技巧优化时间复杂度至O(n),适用于处理大规模树形数据结构。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目


分析

a n s ( x ) = ∑ i = 1 n ∑ j = 0 k S ( k , j ) j ! C d i s ( i , x ) j ans(x)=\sum_{i=1}^n\sum_{j=0}^kS(k,j)j!C_{dis(i,x)}^j ans(x)=i=1nj=0kS(k,j)j!Cdis(i,x)j
= ∑ j = 0 k S ( k , j ) j ! ∑ i = 1 n C d i s ( i , x ) − 1 j + C d i s ( i , x ) − 1 j − 1 =\sum_{j=0}^kS(k,j)j!\sum_{i=1}^nC_{dis(i,x)-1}^j+C_{dis(i,x)-1}^{j-1} =j=0kS(k,j)j!i=1nCdis(i,x)1j+Cdis(i,x)1j1
这就可以dp了,但是这仅仅是根,所以要换根,时间复杂度 O ( n ) O(n) O(n)


代码

#include <cstdio>
#include <cctype>
#include <cstring>
#define rr register
using namespace std;
const int N=50011,mod=10007;
struct node{int y,next;}e[N<<1];
int ls[N],f[N][151],dp[N][151],t[151],k=1,n,m,stir[151][151],fac[151];
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline void add(int x,int y){
    e[++k]=(node){y,ls[x]},ls[x]=k,
	e[++k]=(node){x,ls[y]},ls[y]=k;
}
inline void print(int ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
inline signed mo(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline void dfs1(int x,int fa){
	f[x][0]=1;
	for (rr int i=ls[x];i;i=e[i].next)
	if (e[i].y!=fa){
		dfs1(e[i].y,x);
		for (rr int j=1;j<=m;++j) f[x][j]=mo(f[x][j],mo(f[e[i].y][j],f[e[i].y][j-1]));
		f[x][0]=mo(f[x][0],f[e[i].y][0]);
	}
}
inline void dfs2(int x,int fa){
	for (rr int i=0;i<=m;++i) dp[x][i]=f[x][i];
	if (fa){
		for (rr int i=1;i<=m;++i) t[i]=mo(dp[fa][i]-mo(f[x][i],f[x][i-1]),mod);
		t[0]=mo(dp[fa][0]-f[x][0],mod);
		for (rr int i=1;i<=m;++i) dp[x][i]=mo(dp[x][i],mo(t[i],t[i-1]));
		dp[x][0]=mo(dp[x][0],t[0]);
	}
	for (rr int i=ls[x];i;i=e[i].next)
	    if (e[i].y!=fa) dfs2(e[i].y,x);
}
signed main(){
	n=iut(); m=iut();
	fac[0]=fac[1]=stir[0][0]=stir[1][1]=1;
	for (rr int i=2;i<=m;++i){
		fac[i]=fac[i-1]*i%mod;
		for (rr int j=1;j<=i;++j)
		    stir[i][j]=mo(stir[i-1][j-1],j*stir[i-1][j]%mod);
	}
	for (rr int i=1;i<n;++i) add(iut(),iut());
	dfs1(1,0),dfs2(1,0);
	for (rr int i=1;i<=n;++i){
		rr int ans=0;
		for (rr int j=0;j<=m;++j) ans=mo(ans,stir[m][j]*fac[j]%mod*dp[i][j]%mod);
		print(ans),putchar(10);
	}
	return 0; 
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值