JZOJ6407. 【NOIP2019模拟11.05】小 D 与随机

Description

在这里插入图片描述
n<=5000n<=5000

Solution

  • 神仙计数题。
  • 考虑树形DP。
  • 为了DP的方便,有一种很骚的操作,因为是对于每一个恰好为c个好点的状态求kck^c,我们为了复杂度不好记录c。
  • 但是我们可以将它转化为至少c个好点
    kc=(k1+1)c=i=0cCci(k1)i1ci=i=0cCci(k1)ik^c=(k-1+1)^c=sum_{i=0}^{c}C_c^i*(k-1)^i*1^{c-i}=sum_{i=0}^{c}C_c^i*(k-1)^i
  • 考虑对于每一个合法的好点的选择方案,枚举它的子集,每一个子集的贡献就只用当作(k1)i(k-1)^i,只用我们将k变成k-1,将它当作每多一个好点的贡献进行转移就好了。
  • f[x][j]f[x][j]表示x的子树内,以1~size[x]给它们编号之后,最小的好点的编号为j。
  • 合并的时候类似树形依赖背包,枚举当前size和儿子的size,将它们合并在一起。具体来说就是讨论哪一个里面的最小的好点合并后是最小的,并考虑小于它的点有多少个以及插入的方案数。
  • 注意并不是儿子里面都一定有好点,有可能儿子里没有好点,因为状态没有记录这个东西,所以要单独计算。
  • 最好考虑当前的根是否是好点,对于f[x][i]首先要插在i前面,如果是好点的话要求一个后缀和,当然也要特殊计算儿子里面没有好点,当前根单独一个好点的情况。
  • 最后要考虑整棵树都没有好点的贡献。
  • 具体细节很多。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define maxn 5005
#define ll long long 
#define mo 998244353
using namespace std;

int n,i,j,k,x,y;
int em,e[maxn*2],nx[maxn*2],ls[maxn];
ll ans,K,Kp,C[maxn][maxn],fct[maxn];

void insert(int x,int y){
	em++; e[em]=y; nx[em]=ls[x]; ls[x]=em;
	em++; e[em]=x; nx[em]=ls[y]; ls[y]=em;
}

int sz[maxn],p,q; ll f[maxn][maxn],g[maxn],s0[maxn],s1[maxn];
void dp(int x,int y){
	for(j=0;j<=sz[x];j++) g[j]=f[x][j];
	s0[sz[x]+1]=fct[sz[x]];
	for(j=sz[x];j>=1;j--) s0[j]=(s0[j+1]+f[x][j])%mo;
	s1[sz[y]+1]=fct[sz[y]];
	for(j=sz[y];j>=1;j--) s1[j]=(s1[j+1]+f[y][j])%mo;
	for(j=0;j<=sz[x]+sz[y];j++) f[x][j]=0;
	for(p=1;p<=sz[x];p++) for(k=0;k<=sz[y];k++)
		f[x][p+k]+=C[p+k-1][p-1]*C[sz[x]+sz[y]-p-k][sz[x]-p]%mo*g[p]%mo*s1[k+1]%mo;
	for(q=1;q<=sz[y];q++) for(k=0;k<=sz[x];k++)
		f[x][q+k]+=C[q+k-1][q-1]*C[sz[x]+sz[y]-q-k][sz[y]-q]%mo*f[y][q]%mo*s0[k+1]%mo;
	sz[x]+=sz[y];
	for(j=1;j<=sz[x];j++) f[x][j]%=mo;
}

void dfs(int x,int p){
	for(int i=ls[x];i;i=nx[i]) if (e[i]!=p)
		dfs(e[i],x),dp(x,e[i]);
	sz[x]++;
	ll sum=0;
	for(int j=sz[x];j;j--) {
		(sum+=f[x][j])%=mo;
		f[x][j]=(f[x][j-1]*(j-1)%mo+sum*K%mo+fct[sz[x]-1]*K%mo)%mo;
	} 
}

int main(){
	scanf("%d%lld",&n,&K),K--;
	for(i=1;i<n;i++) scanf("%d%d",&x,&y),insert(x,y);
	C[0][0]=1,fct[0]=1;
	for(i=1;i<=n;i++){
		C[i][0]=1,fct[i]=fct[i-1]*i%mo;
		for(j=1;j<=i;j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%mo;
	}
	dfs(1,0);
	ll ans=fct[n];
	for(i=1;i<=n;i++) ans+=f[1][i];
	printf("%lld",ans%mo);
}

原文地址:https://www.cnblogs.com/DeepThinking/p/13090920.html