您的位置:首页 > 其它

[Codeforces 662A]Gambling Nim

2018-07-08 19:24 337 查看

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; }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: