[Codeforces 662A]Gambling Nim

Descripeion

题库链接

\(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; }

猜你喜欢

转载自www.cnblogs.com/NaVi-Awson/p/9281001.html
Nim
今日推荐