题意:一棵n=2mn=2mn=2m个点的树,mmm个白点和mmm个黑点。对于k∈[0,n]k\in [0,n]k∈[0,n],求出 把点黑白两两配对使得恰好有kkk对点有祖孙关系 的方案数 模998244353998244353998244353。
n≤5000n \leq 5000n≤5000
见到恰好考虑容斥(并不
我们钦定kkk对点有祖孙关系,其他点随便匹配
这个是个经典的树上背包,设fu,kf_{u,k}fu,k表示uuu的子树中钦定kkk对的方案数
直接把子树搓起来,然后算上匹配根的方案
看上去是O(n3)O(n^3)O(n3),实际上控下上下界可以做到O(n2)O(n^2)O(n2)
证明是一次的复杂度是∑x,y∈son(u)sizxsizy\sum_{x,y\in son(u) }siz_xsiz_y∑x,y∈son(u)sizxsizy,抽象成把所有跨子树的点对遍历一次,显然一个点对只会在lca处被遍历
算完后记得乘上其它点随便匹配的方案,即一个阶乘
然后二项式反演即可
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <vector>
#define MAXN 5005
using namespace std;
inline int read()
{int ans=0;char c=getchar();while (!isdigit(c)) c=getchar();while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();return ans;
}
const int MOD=998244353;
inline int add(const int& x,const int& y){return x+y>=MOD? x+y-MOD:x+y;}
typedef long long ll;
inline int qpow(int a,int p)
{int ans=1;while (p){if (p&1) ans=(ll)ans*a%MOD;a=(ll)a*a%MOD;p>>=1;}return ans;
}
char s[MAXN];
vector<int> e[MAXN];
int col[MAXN],cnt[MAXN][2],lim[MAXN],dp[MAXN][MAXN];
void dfs(int u,int f)
{cnt[u][col[u]]=1;dp[u][0]=1; for (int i=0;i<(int)e[u].size();i++)if (e[u][i]!=f){dfs(e[u][i],u);cnt[u][0]+=cnt[e[u][i]][0],cnt[u][1]+=cnt[e[u][i]][1];for (int x=min(cnt[u][0],cnt[u][1]);x>=1;x--)for (int k=max(x-lim[u],1);k<=x&&k<=min(cnt[e[u][i]][0],cnt[e[u][i]][1]);k++)dp[u][x]=(dp[u][x]+(ll)dp[u][x-k]*dp[e[u][i]][k])%MOD;lim[u]=min(cnt[u][0],cnt[u][1]); }for (int x=min(cnt[u][0],cnt[u][1]);x>=0;x--)dp[u][x+1]=(dp[u][x+1]+(ll)(cnt[u][col[u]^1]-x)*dp[u][x])%MOD;
}
int fac[MAXN],finv[MAXN],ans[MAXN];
inline int C(const int& n,const int& m){return (ll)fac[n]*finv[m]%MOD*finv[n-m]%MOD;}
int main()
{int n=read();int m=n/2;scanf("%s",s+1);fac[0]=1;for (int i=1;i<=n;i++) fac[i]=(ll)fac[i-1]*i%MOD;finv[n]=qpow(fac[n],MOD-2);for (int i=n-1;i>=0;i--) finv[i]=(ll)finv[i+1]*(i+1)%MOD;for (int i=1;i<=n;i++) col[i]=s[i]-'0';for (int i=1;i<n;i++){int u,v;u=read(),v=read();e[u].push_back(v),e[v].push_back(u);}dfs(1,0);for (int i=0;i<=m;i++) dp[1][i]=(ll)dp[1][i]*fac[m-i]%MOD;for (int i=0;i<=m;i++)for (int j=i;j<=m;j++)ans[i]=add(ans[i],(ll)(((j^i)&1)? (MOD-C(j,i)):C(j,i))*dp[1][j]%MOD);for (int i=0;i<=m;i++) printf("%d\n",ans[i]);return 0;
}