Codeforces

来源:互联网 发布:linux系统基础教程 编辑:程序博客网 时间:2024/05/30 04:38

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);    }}
原创粉丝点击