Codeforces - 662A. Gambling Nim - 博弈

26 篇文章 0 订阅
3 篇文章 0 订阅

Gambling Nim

题目链接

分类bitmask math matrices probabilities

1.题意概述

  • 给你 n(1n500000) 张卡片,每张卡片的两个面(正面 ai 反面 bi )都有写数字,每个面都有0.5的概率正面,卡牌正反面的概率相互独立,求把所有卡牌正面数字( ci=ai or bi )拿来玩Nim游戏,先手必胜的概率。

2.解题思路

  • 什么是Nim游戏

    有若干堆石子,每堆石子的数量都是有限的,合法的移动是“选择一堆石子并拿走若干颗(不能不拿)”,如果轮到某个人时所有的石子堆都已经被拿空了,则判负(因为他此刻没有任何合法的移动)。

  • Nim游戏结论:所有数字异或和为0,先手必败。

  • 因此,这个问题实际上是让我们计算有多少种情况使得所有正面朝上的数字异或和为0。我们不妨假设 S=a1a2...an 并且 ci=aibi ,假设卡片 j1,j2,...jk 都是 bi 朝上,剩下卡片都是 ai 朝上,那么现在总的异或和就是 Scj1cj2...cjk ,我们目标就是要找到一些 ci 的子集,使得他们的异或和为 S (因为一个数与自身异或为0),又因为ci=(cicj)cj,因此我们可以自由通过用 cicj 来代替 cj (因为 cicj=ci(cicj) !),因此我们考虑如何简化 ci 集合:

    1. 选择出二进制位某一个位为1的数 ci
    2. 使用 cicj 来替换所有的 ci
    3. 重复上述步骤。

    最终,我们会得到包括k个0和n-k个数的集合,下面问题在于如何检测这个集合是不是 S 的集合?我们可以按数位来讨论,而所有子集的数量一定是2k对于每个 nk 个不是零的数字概率就是 2k2n ,这是先手必败,那么先手必胜就是 2nk12nk ,因为是减一,所以分数一定是最简的,无需化简!

3.AC代码

int cnt;
ll a[maxn], b[maxn], c[maxn],digit[110];
bool add(ll x) {
    rep(i, 1, cnt + 1)
        if ((digit[i] ^ x) < x)
            x = digit[i] ^ x;
        if (x) {
            digit[++cnt] = x;
            return 1;
        }
        return 0;
}
inline void solve() {
    int n;
    scanf("%d", &n);
    ll res = 0;
    rep(i, 1, n + 1) {
        scanf("%I64d%I64d", &a[i], &b[i]);
        c[i] = a[i] ^ b[i];
        add(c[i]);
        res ^= a[i];
    }
    if (add(res)) puts("1/1");
    else {
        ll ans = 1LL << cnt;
        printf("%I64d/%I64d\n", ans - 1, ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值