P6478
容易想到树形dp,设 \(f_{i,j}\) 表示以 \(i\) 为根的子树中匹配了至少 \(j\) 对 0 和 1 的方案数。
转移就是树卷积,先考虑当前点不选的情况,\(f_{i,a+b}=\sum f_{i,a}f_{j,b}\)。
然后考虑当前点选择的情况,预处理出子树中 0 和 1 的个数 \(c_{u,0},c_{u,1}\),则 \(f_{i,j}\leftarrow f_{i,j-1}(c_{u,1-col_u}-(j-1))\)。
最后二项式反演即可得到恰好的方案数。
#include<bits/stdc++.h>
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
#define pb push_back
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int N=5005;
int n;
char s[N];
vector<int>g[N];
int c0[N],c1[N];
ll f[N][N],sm[N];
void dfs(int u,int fa){
if(s[u]=='0'){
c0[u]++;
}
else{
c1[u]++;
}
int cnt=0;
for(auto v:g[u]){
if(v==fa)continue;
cnt++;
dfs(v,u);
c0[u]+=c0[v];
c1[u]+=c1[v];
}
f[u][0]=1;
if(!cnt){
return ;
}
int cur=1;
for(auto v:g[u]){
if(v==fa)continue;
for(int i=0;i<=cur+c0[v]+c1[v];i++)sm[i]=0;
for(int x=0;x<=cur;x++){
for(int y=0;y<=c0[v]+c1[v];y++){
sm[x+y]=(sm[x+y]+f[u][x]*f[v][y]%mod)%mod;
}
}
for(int i=0;i<=cur+c0[v]+c1[v];i++)f[u][i]=sm[i];
cur+=c0[v]+c1[v];
}
for(int i=cur;i>=1;i--){
if(s[u]=='0'){
f[u][i]=(f[u][i]+f[u][i-1]*(c1[u]-(i-1))%mod)%mod;
}
else{
f[u][i]=(f[u][i]+f[u][i-1]*(c0[u]-(i-1))%mod)%mod;
}
}
}
ll ksm(ll a,ll b,ll p){
a=a%p;
ll r=1;
while(b){
if(b&1){
r=r*a%p;
}
a=a*a%p;
b>>=1;
}
return r%p;
}
ll fac[N],inv[N];
void Init(int n){
fac[0]=1;
for(int i=1;i<=n;i++){
fac[i]=fac[i-1]*i%mod;
}
inv[n]=ksm(fac[n],mod-2,mod);
for(int i=n;i>=1;i--){
inv[i-1]=inv[i]*i%mod;
}
}
ll C(int n,int m){
if(n<m)return 0;
return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
ll h[N];
int main(){
scanf("%d",&n);
Init(n);
scanf("%s",s+1);
int u,v;
rep(i,1,n-1){
scanf("%d %d",&u,&v);
g[u].pb(v);
g[v].pb(u);
}
dfs(1,-1);
for(int i=0;i<=n/2;i++){
f[1][i]=f[1][i]*fac[n/2-i]%mod;
}
for(int i=0;i<=n/2;i++){
for(int j=i;j<=n/2;j++){
if((j-i)%2==0){
h[i]=(h[i]+f[1][j]*C(j,i)%mod)%mod;
}
else{
h[i]=(h[i]-f[1][j]*C(j,i)%mod+mod)%mod;
}
}
}
for(int i=0;i<=n/2;i++){
printf("%lld\n",h[i]);
}
return 0;
}