题解 P6478 【[NOI Online #2 提高组]游戏】
Daniel_yuan · · 题解
不知道怎么公式渲染炸了……可能是不支持两个美元的公式吧(已尽力修改)……可以手动进入博客看看。(造成的不便敬请谅解)
先安利一个二项式反演的公式:
注:十分抱歉,这个公式之前是错的,请注意右侧
不会证明,但是会用就行。
这个题先设
考虑怎么转移,先不考虑
直接枚举显然不行,考虑每遍历一棵子树就把答案并进去,那么就是
这个式子乍一看是
最后的时候再把
至此我们就可以在
我们先设
但是这样子会算重,因为自由组合也可能贡献非平局对。我们设
举个栗子,假设
那么我们就有
不太会证明为什么可以这样,我只能通过这种套式子的方法来做,而且我认为
代码如下:考场代码求轻喷
#include <bits/stdc++.h>
#define RI register int
typedef long long LL;
#define int LL
#define FILEIO(name) freopen(name".in", "r", stdin), freopen(name".out", "w", stdout);
using namespace std;
int const mod = 998244353, MAXN = 5005;
int f[MAXN][MAXN], g[MAXN];
int A[MAXN], B[MAXN];
char s[MAXN];
struct Edges { int to, next; } e[MAXN * 2];
int head[MAXN], tot;
int size[MAXN];
int frac[MAXN], invfrac[MAXN], inv[MAXN];
inline LL C(int n, int m) { return frac[n] * invfrac[m] % mod * invfrac[n - m] % mod; }
void Init(int Max) {
frac[0] = invfrac[0] = 1;
frac[1] = invfrac[1] = inv[1] = 1;
for (RI i = 2; i <= Max; ++i) {
frac[i] = frac[i - 1] * i % mod;
inv[i] = (mod - mod / i) * inv[mod % i] % mod;
invfrac[i] = invfrac[i - 1] * inv[i] % mod;
}
}
inline void addedge(int from, int to) {
e[++tot] = (Edges){to, head[from]};
head[from] = tot;
e[++tot] = (Edges){from, head[to]};
head[to] = tot;
}
void Dfs(int now, int fa) {
if (s[now] == '0') ++A[now];
else ++B[now];
for (RI i = head[now]; i; i = e[i].next)
if (e[i].to != fa) {
Dfs(e[i].to, now);
A[now] += A[e[i].to];
B[now] += B[e[i].to];
}
}
void Solve(int now, int fa) {
size[now] = 0;
f[now][0] = 1;
for (RI i = head[now]; i; i = e[i].next)
if (e[i].to != fa) {
Solve(e[i].to, now);
for (RI j = 0; j <= size[now] + size[e[i].to]; ++j) g[j] = 0;
for (RI j = 0; j <= size[now]; ++j)
for (RI k = 0; k <= size[e[i].to]; ++k)
g[j + k] = (g[j + k] + f[now][j] * f[e[i].to][k] % mod) % mod;
size[now] += size[e[i].to];
for (RI j = 0; j <= size[now]; ++j)
f[now][j] = g[j];
}
if (s[now] == '0') {
for (RI i = size[now]; i >= 0; --i)
if (B[now] - i > 0)
f[now][i + 1] = (f[now][i + 1] + f[now][i] * (B[now] - i) % mod) % mod;
if (f[now][size[now] + 1]) ++size[now];
}
else {
for (RI i = size[now]; i >= 0; --i)
if (A[now] - i > 0)
f[now][i + 1] = (f[now][i + 1] + f[now][i] * (A[now] - i) % mod) % mod;
if (f[now][size[now] + 1]) ++size[now];
}
}
signed main() {
FILEIO("match");
ios :: sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n; cin >> n; Init(n);
cin >> (s + 1);
for (RI i = 1, x, y; i < n; ++i)
cin >> x >> y, addedge(x, y);
Dfs(1, 0);
Solve(1, 0);
for (RI i = 0; i <= size[1]; ++i)
g[i] = f[1][i] * frac[n / 2 - i] % mod;
for (RI i = 0; i <= n / 2; ++i) {
int Ans = 0;
for (RI j = i, op = 1; j <= size[1]; ++j, op = op * (mod - 1) % mod)
Ans = (Ans + op * C(j, i) % mod * g[j] % mod) % mod;
cout << Ans << " ";
}
cout << endl;
return 0;
}
// created by Daniel yuan