[2020 NOI Online]游戏
Link
Desciption
树上有 \(2n\) 个点,每个点属于两个集合中的一个,且集合大小为 \(n\)。将两个集合里的点配对,使得恰好有 \(k\) 对从属关系,即一个点在另一个点的子树里。
Solution
和已经没有什么好害怕的了这道题很像,都是点的配对,然后问有多少形成某种偏序关系的方案。前者的偏序关系就是实数的比较,所以容易想到按值排序,然后线性 dp。
类似的,这道题的偏序关系构成一棵树,所以想到用树形 dp 统计方案。定义 \(dp_{u,j}\) 表示在以 \(u\) 为根的子树中,有 \(j\) 对从属关系的方案数。
那么容易想到有两种转移方式。第一种是所有从属关系直接从子节点获得,背包即可。第二种是节点 \(u\) 和子树内的某个点配对,考虑到状态 \(dp_{u,j}\) 保证了至少有 \(j\) 对点被选,也即一个集合被选了 \(j\) 个元素,那么剩下的可选的元素个数就可以通过集合大小减去 j 来获得。而集合大小容易统计。
钦定 \(j\) 对从属关系的可重方案就为 \(F_j=dp_{1,j}\times(n-j)!\),反演一下即为答案。
#include<stdio.h>
#define N 7007
#define ll long long
#define Mod 998244353
inline int read(){
int x=0,flag=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
return flag? x:-x;
}
int n;
char S[N];
struct E{
int next,to;
}e[N<<1];
int head[N],cnt=0,sz[N],s[2][N],fa[N],m;
ll fac[N],inv[N];
ll qpow(ll x,ll y){
ll ret=1,tot=0;
while(y>=(1ll<<tot)){
if(y&(1ll<<tot)) ret=ret*x%Mod;
x=x*x%Mod,tot++;
}
return ret;
}
inline void add(int id,int to){
e[++cnt]=(E){head[id],to};
head[id]=cnt;
}
ll dp[N][N];
void dfs(int u){
dp[u][0]=1;
sz[u]=1,s[u][S[u]-'0']=1;
static ll tmp[N];
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(v==fa[u]) continue;
fa[v]=u,dfs(v);
for(int j=0;j<=m;j++) tmp[j]=0;
for(int j=sz[u];~j;j--)
for(int k=sz[v];~k;k--)
tmp[j+k]=(tmp[j+k]+dp[u][j]*dp[v][k]%Mod)%Mod;
for(int j=0;j<=m;j++) dp[u][j]=tmp[j];
sz[u]+=sz[v];
s[u][0]+=s[v][0],s[u][1]+=s[v][1];
}
int to=(S[u]-'0')^1;
for(int i=s[u][to];i;i--)
dp[u][i]=(dp[u][i]+dp[u][i-1]*(s[u][to]-i+1)%Mod)%Mod;
}
ll C(int x,int y){return x<y? 0:fac[x]*inv[y]%Mod*inv[x-y]%Mod;}
int main(){
n=read(),m=n>>1;
scanf("%s",S+1);
for(int i=1;i<n;i++){
int u=read(),v=read();
add(u,v),add(v,u);
}
dfs(1); fac[0]=1;
for(int i=1;i<=m;i++) fac[i]=fac[i-1]*i%Mod;
inv[m]=qpow(fac[m],Mod-2);
for(int i=m-1;~i;i--) inv[i]=inv[i+1]*(i+1)%Mod;
for(int i=0;i<=m;i++) dp[1][i]=dp[1][i]*fac[m-i]%Mod;
for(int i=0;i<=m;i++){
ll ans=0;
for(int k=i,op=1;k<=m;k++,op=-op)
ans=(ans+op*C(k,i)*dp[1][k]%Mod+Mod)%Mod;
printf("%lld\n",ans);
}
}