[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; }
博主蒟蒻,随意转载。但必须附上原文链接:http://www.cnblogs.com/NaVi-Awson/,否则你会终生找不到妹子!!!