[Codeforces 662A]Gambling Nim

Description

题库链接

$n$ 张卡牌,正反两面有两个数字。每一面的概率都为 $0.5$ 。将所有卡片的值异或起来,求异或值不为 $0$ 的概率。

$1\leq n\leq 500000$

Solution

先考虑异或值为 $0$ 的情况。

假设一张卡片上两个数字为 $a,b$ ,我们令 $sum=\bigoplus\limits_{1\leq i\leq n}a_i$ ,维护一个关于 $c=a\oplus b$ 的线性基。

相当于要求 $c$ 有多少个子集异或出来为 $sum$ 。

对于 $sum$ ,若他不能用线性基表示,即总的异或和恒不为 $0$ ,答案就为 $1/1$ 。

如果能用线性基表示,所有异或和为 $sum$ 的情况数就为 $2^{n-tot}$ ( $tot$ 为线性基的大小 )。异或和为 $0$ 的概率为 $\frac{2^{n-tot}}{2^n} = \frac{1}{2^{tot}}$ 。

那么异或和不为 $0$ 的概率为 $\frac{2^{tot-1}}{2^{tot}}$ 。

Code

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 500000+5;

int n;
ll a, b, sum, bin[65];
struct base {
    ll a[65];
    void insert(ll x) {
        for (int i = 63; i >= 0; i--)
            if (x&bin[i]) {
                if (!a[i]) {a[i] = x; break; }
                else x ^= a[i];
            }
    }
    bool find(ll x) {
        for (int i = 63; i >= 0; i--)
            if (x&bin[i]) x ^= a[i];
        return !x;
    }
    int size() {
        int cnt = 0;
        for (int i = 63; i >= 0; i--)
            if (a[i]) ++cnt;
        return cnt;
    }
}B;

void work() {
    scanf("%d", &n); bin[0] = 1;
    for (int i = 1; i < 64; i++) bin[i] = (bin[i-1]<<1);
    for (int i = 1; i <= n; i++) {
        scanf("%I64d%I64d", &a, &b);
        B.insert(a^b); sum ^= a;
    }
    if (!B.find(sum)) puts("1/1");
    else {
        int cnt = B.size();
        printf("%I64d/%I64d\n", bin[cnt]-1, bin[cnt]);
    }
}
int main() {work(); return 0; }
posted @ 2018-07-08 19:24  NaVi_Awson  阅读(235)  评论(0编辑  收藏  举报