[博弈论] [计数] AT_arc191_e [ARC191E] Unfair Game

posted on 2025-05-13 01:55:04 | under | source

题意:\(n\) 个袋子,第 \(i\) 个袋子有 \(a_i\) 个金币 \(b_i\) 个银币。游戏开始前由你决定每个袋子给你还是给对手。由你先手,每轮选择一个袋子将 \(1\) 个金币转化为若干银币(你可转化 \(x\) 个、对手可转化 \(y\) 个),或者取走一个银币;然后把袋子给对面。无法操作者输。求有多少种初始局面使得你必胜?\(n\le 2\times 10^5,a,b,x,y\le 10^9\)

显然每个袋子互相独立,那么我们可以将博弈的过程视为轮流对每个袋子进行博弈,直到取完再到下一个袋子。

故考虑一个袋子的博弈:

  • 没有金币:判定 \(b_i\) 的奇偶性即可。
  • 存在金币:将 \(x\gets x+1,y\gets y+1\),把金币操作改写为:先转化金币,然后取走一个银币。这样的好处是金银币操作相对独立了。

然后对 \(x,y\) 的奇偶性分讨:

  • \(x,y\) 奇偶性相同:判定 \(a_i x+b_i\) 的奇偶性即可。
  • 反之:发现为奇数者占优,因为他可以改变胜负,若本来就胜利那么不操作金币、否则操作金币让自己变成胜利方。注意有特殊情况,若 \(a_i=1\),先手可以抢先操作金币让对面改变不了胜负。其它情况结论都是成立的。

综上,我们将袋子分为:先手必胜、后手必胜、我必胜、对手必胜。

然后考虑多个袋子就简单了,过程就是我先选一个袋子博弈,然后败者再选一个袋子博弈。那么会导致失败的袋子就相当于跳过,不用管他们。因此我手上的胜利袋子大于对手的胜利袋子就赢了。

方案容易组合数计算,最后处理一下后缀和就能 \(O(n)\) 了。

代码

#include<bits/stdc++.h>
using namespace std;

#define int long long
#define ADD(a, b) a = (a + b) % mod
const int N = 2e5 + 5, mod = 998244353;
int n, x, y, a[N], b[N], c1, c2, c3, c4, jc[N], jcinv[N], ans, suf[N];

inline int qstp(int a, int k) {int res = 1; for(; k; a = a * a % mod, k >>= 1) if(k & 1) res = res * a % mod; return res;}
inline int C(int n, int m){
    return jc[n] * jcinv[m] % mod * jcinv[n - m] % mod;
}
inline int Csum(int lim){
    if(lim >= c3 + c4) return 0;
    return suf[max(0ll, lim + 1)];
}
signed main(){
    jc[0] = jcinv[0] = 1;
    for(int i = 1; i < N; ++i) jcinv[i] = qstp(jc[i] = jc[i - 1] * i % mod, mod - 2);
    cin >> n >> x >> y;
    ++x, ++y;
    for(int i = 1; i <= n; ++i){
        scanf("%lld%lld", &a[i], &b[i]);
        if(!a[i]) c1 += b[i] % 2, c2 += 1 - b[i] % 2;
        else if(x % 2 == y % 2){
            if((a[i] * x + b[i]) % 2) ++c1;
            else ++c2; 
        }
        else{
            if(x % 2){
                if(a[i] == 1 && b[i] & 1) ++c1;
                else ++c3; 
            }
            else{
                if(a[i] == 1 && b[i] & 1) ++c1;
                else ++c4;
            }
        }
    }
    for(int i = c3 + c4; ~i; --i) suf[i] = (suf[i + 1] + C(c3 + c4, i)) % mod;
    for(int i = 0; i <= c1; ++i)
            ADD(ans, C(c1, i) * Csum(c1 + c4 - 2 * i) % mod);
    cout << ans * qstp(2, c2) % mod;
    return 0;
}
posted @ 2026-01-12 20:07  Zwi  阅读(0)  评论(0)    收藏  举报