游戏【坑】

link

容斥好题。

由子集反演公式可得:

\[f(n)=\sum\limits_{i=n}^mC_i^ng(i)\iff g(n)=\sum\limits_{i=n}^m(-1)^{i-n}C_i^nf(i) \]

用f函数代表有至少n个非平局的方案,g代表恰好有n个平局的方案,发现f和g满足前面的方程,于是他们也应当满足后面的关系。于是题目让求g的函数值就转化成了求f的函数值。而f的函数值可以通过树形DP来解决。一棵树内非平局的数量只可能来自于两个地方,一个是某个子树内部的答案,这一部分相当于一个树上背包;另一种是树根和某个点形成的答案,这一部分最后累加即可。要注意的是树上背包的枚举顺序,否则复杂度是假的。f的函数值就是整棵树树根的答案,套上面的公式计算即可。

#include<bits/stdc++.h>
//#define zczc
#define int long long
const int N=5010;
const int mod=998244353;
using namespace std;
inline void read(int &wh){
    wh=0;int f=1;char w=getchar();
    while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
    while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
    wh*=f;return;
}
inline int max(int s1,int s2){
	return s1<s2?s2:s1;
}

inline int qpow(int s1,int s2){
	if(s2==1)return s1;
	int an=qpow(s1,s2>>1);
	if(s2&1)return an*an%mod*s1%mod;
	else return an*an%mod;
}

struct edge{
	int t,next;
}e[N<<1];
int head[N],esum;
inline int add(int fr,int to){
	e[++esum]=(edge){to,head[fr]};head[fr]=esum;
}

char w[N];
int m,num,a[N],cnt[N],size[N],f[N][N],ff[N];
void dfs(int wh,int fa){
	size[wh]=f[wh][0]=1;
	for(int i=head[wh],th;i;i=e[i].next){
		if((th=e[i].t)==fa)continue;
		dfs(th,wh);cnt[wh]+=cnt[th];
		for(int j=0;j<=size[wh]+size[th];j++)ff[j]=0;
		for(int j=0;j<=size[wh]/2;j++){
			for(int k=0;k<=size[th]/2;k++){
				if(f[th][k]==0)break;
				ff[j+k]=(ff[j+k]+f[wh][j]*f[th][k])%mod;
			}
		}
		size[wh]+=size[th];
		for(int j=0;j<=size[wh];j++)f[wh][j]=ff[j];
		/*
		for(int k=size[wh]/2;k>=0;k--){
			for(int j=1;j<=size[th]/2&&j<=k;j++){
				if(f[th][j]==0)break;
				f[wh][k]+=(f[wh][k-j]*f[th][j])%mod;
			}
		}
		*/
	}
	if(a[wh])num=size[wh]-cnt[wh];
	else num=cnt[wh];
	for(int i=size[wh];i;i--){
		f[wh][i]+=f[wh][i-1]*max(num-i+1,0);
		f[wh][i]%=mod;
	}
}

int g[N],u[N],v[N],an[N];

inline int C(int s1,int s2){
	return u[s1]*v[s2]%mod*v[s1-s2]%mod;
}

signed main(){
	
	#ifdef zczc
	freopen("in.txt","r",stdin);
	#endif
	
	read(m);int s1,s2;
	scanf("%s",w+1);
	for(int i=1;i<=m;i++)cnt[i]=a[i]=w[i]=='1';//子树内A的个数
	for(int i=1;i<m;i++){
		read(s1);read(s2);
		add(s1,s2);add(s2,s1);
	}
	dfs(1,0);u[0]=1;
	for(int i=1;i<=m/2;i++)u[i]=u[i-1]*i%mod;
	for(int i=0;i<=m/2;i++)g[i]=f[1][i]*u[m/2-i]%mod;u[0]=v[0]=1;
	for(int i=1;i<=m/2;i++){
		u[i]=u[i-1]*i%mod;
		v[i]=qpow(u[i],mod-2);
	}
	for(int i=0;i<=m/2;i++){
		for(int j=i;j<=m/2;j++){
			an[i]+=((j-i&1)?-1:1)*C(j,i)*g[j]%mod;
			an[i]%=mod;
		}
		printf("%lld\n",(an[i]%mod+mod)%mod);
	}
	
	return 0;
}
posted @ 2022-06-29 19:40  Feyn618  阅读(8)  评论(0编辑  收藏  举报