【题解】[NOI Online #2 提高组] 游戏
程序员文章站
2024-03-17 16:30:52
...
提高组 跑步
小 和小 正在玩一个游戏:有一棵包含 个点的有根树(点从 编号),它的根是 号点,初始时两人各拥有 个点。游戏的每个回合两人都需要选出一个自己拥有且之前未被选过的点,若对手的点在自己的点的子树内,则该回合自己获胜;若自己的点在对方的点的子树内,该回合自己失败;其他情况视为平局。游戏共进行 回合。
作为旁观者的你只想知道,在他们随机选点的情况下,第一次非平局回合出现时的回合数的期望值。
为了计算这个期望,你决定对于 ,计算出非平局回合数为 的情况数。两种情况不同当且仅当存在一个小 拥有的点 ,小 在 被小 选择的那个回合所选择的点不同。
由于情况总数可能很大,你只需要输出答案对 取模后的结果。
设 表示 子树内,非平局对为 对的方案数,有状态转移方程
直接枚举显然不行,考虑每遍历一棵子树就把答案并进去,则有
这个式子看起来是 的,但是如果卡 的上界就会变成 ,证明为每个点 只会对 有一个复杂度的贡献。
最后的时候再把 个贡献加进去,分别设 为 子树内小 的节点个数,假设当前节点是小 的节点,那么就有
即之前有 个非平局对,那么就会消耗掉 个小 的节点,所以现在还剩 个小 的节点可用。类似的,如果当前的节点是小 的节点就把上文的 换成 即可。
至此我们就可以在 的时间复杂度内算出所有子树内选 个非平局对的方案数。
我们先设 ,也就是说先固定选出来的 个非平局对,然后把剩下的*组合。
但是这样会算重,因为*组合也可能贡献非平局对。我们设 表示恰好有 个非平局对的方案数,可以发现对于每个 ,它对 的贡献是 ,那么我们就有 。
这个可以二项式反演一下,有公式
套进去便得到了 。
#define int long long
#define mod 998244353
int n,x,y,num_edge,fac[5001],c[5001][5001],head[5001],f[5001][5001],g[5001],siz[5001],s[5001];
char str[5001];
struct EDGE {
int v,next;
}E[10001];
void ADD_EDGE(int u,int v) {
E[++num_edge].v=v;
E[num_edge].next=head[u];
head[u]=num_edge;
}
void DFS(int u,int fath) {
siz[u]=1;
s[u]=str[u]-'0';
f[u][0]=1;
for (int i=head[u];i;i=E[i].next)
if (E[i].v!=fath) {
DFS(E[i].v,u);
for (int j=0;j<=siz[u]+siz[E[i].v];j++) g[j]=0;
for (int j=0;j<=min(siz[u],n/2);j++)
for (int k=0;k<=min(siz[E[i].v],n/2-j);k++)
(g[j+k]+=f[u][j]*f[E[i].v][k]%mod)%=mod;
for (int j=0;j<=siz[u]+siz[E[i].v];j++) f[u][j]=g[j];
siz[u]+=siz[E[i].v];
s[u]+=s[E[i].v];
}
for (int i=min(s[u],siz[u]-s[u]);i>=1;i--)
if (str[u]=='1') (f[u][i]+=f[u][i-1]*(siz[u]-s[u]-i+1))%=mod;
else (f[u][i]+=f[u][i-1]*(s[u]-i+1))%=mod;
}
signed main() {
scanf("%lld%s",&n,str+1);
fac[0]=1;
for (int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
c[0][0]=1;
for (int i=1;i<=n;i++) c[i][0]=1;
for (int i=1;i<=n;i++)
for (int j=1;j<=i;j++)
c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
for (int i=1;i<n;i++) {
scanf("%d%d",&x,&y);
ADD_EDGE(x,y);
ADD_EDGE(y,x);
}
DFS(1,0);
for (int i=0;i<=n/2;i++) (f[1][i]*=fac[n/2-i])%=mod;
for (int i=0;i<=n/2;i++) {
int ans=0;
for (int j=i;j<=n/2;j++)
if ((j-i)&1) ((ans-=c[j][i]*f[1][j]%mod)+=mod)%=mod;
else (ans+=c[j][i]*f[1][j]%mod)%=mod;
printf("%lld\n",ans);
}
return 0;
}