AT ARC191E Unfair Game
cnblogs。
发现操作同一个包的人一定是交错的,这说明每个包初始给每个人时,每个包的胜负关系已经确定了且相互独立。
其实这里还没有对胜负进行定义,定义一个人对于一个包赢当且仅当最后是这个人取走的包内最后一枚硬币。
于是来考虑如何求解一个包的胜负情况。
因为金币能够转化为银币,而银币不能进一步转化,于是看起来金币更为关键。
首先若金币数为
此时会发现我们只关心最后银币数的奇偶,于是不妨令
这样的好处是每一步都必定会取走一枚银币,不同点只在于要不要进行一次转化操作。
那么若
若
于是可以得出,只要能给到奇数操作,那么谁为奇数谁赢。
根据上述结论进行讨论,能够发现:
- 当金币数为
1 时且银币数为奇数时,先手必胜(偶先手可以直接用掉金币,奇先手就不用说了)。 - 否则,谁为奇数谁赢。
根据上述讨论,发现每个包的胜负情况分为
- 后手必胜。
- 先手必胜。
- A 必胜。
- B 必胜。
发现一个人拿到了非自己必胜的包,结果就是用掉这个包;如果拿到了自己必胜的包,结果就是用掉这个包并调换先后手。
于是在考虑最终的胜负情况时只需要考虑自己必胜的包的个数。
记四种情况的包数分别为
枚举 A 拿到的先手必胜包数
接下来考虑化简条件
根据前面的分析,能够知道 A 必胜 B 必胜中,一定是谁为奇谁必胜,这说明
于是考虑分讨:
-
对 $b$ 前缀和,枚举 $i$ 即可。 -
对 $a$ 后缀和,枚举 $i$ 即可。
时间复杂度为
#include <bits/stdc++.h>
#include <atcoder/all>
using mint = atcoder::modint998244353;
constexpr int N = 2e5;
mint fac[N + 1], ifac[N + 1];
inline mint binom(int n, int m) {
return fac[n] * ifac[n - m] * ifac[m];
}
int cnt[4];
mint sum[N + 2];
int main() {
fac[0] = 1;
for (int i = 1; i <= N; i++) fac[i] = fac[i - 1] * i;
ifac[N] = fac[N].inv();
for (int i = N; i >= 1; i--) ifac[i - 1] = ifac[i] * i;
int n, x, y;
scanf("%d%d%d", &n, &x, &y);
x = (x + 1) % 2, y = (y + 1) % 2;
for (int a, b; n--; ) {
scanf("%d%d", &a, &b);
b %= 2;
if (a == 0 || x == y) {
cnt[(a * x + b) % 2]++;
continue;
}
if (a == 1 && b == 1) {
cnt[1]++;
} else {
cnt[2 + y]++;
}
}
mint ans = 0;
if (cnt[2] == 0) {
sum[0] = 1;
for (int i = 1; i <= N; i++) {
sum[i] = sum[i - 1] + (i <= cnt[3] ? binom(cnt[3], i) : 0);
}
for (int i = 0; i <= cnt[1]; i++) {
if (i * 2 - cnt[1] > 0) {
ans += sum[i * 2 - cnt[1] - 1] * binom(cnt[1], i);
}
}
} else {
for (int i = cnt[2]; i >= 0; i--) {
sum[i] = sum[i + 1] + binom(cnt[2], i);
}
for (int i = 0; i <= cnt[1]; i++) {
ans += sum[std::max(cnt[1] - i * 2 + 1, 0)] * binom(cnt[1], i);
}
}
while (cnt[0]--) ans *= 2;
printf("%d\n", ans.val());
return 0;
}