您的位置:首页 > 其它

Codeforces - 662A. Gambling Nim - 博弈

2017-08-13 11:23 621 查看

Gambling Nim

题目链接

分类
bitmask
math
matrices
probabilities


1.题意概述

给你n(1≤n≤500000)张卡片,每张卡片的两个面(正面ai反面bi)都有写数字,每个面都有0.5的概率正面,卡牌正反面的概率相互独立,求把所有卡牌正面数字(ci=ai or bi)拿来玩Nim游戏,先手必胜的概率。

2.解题思路

什么是Nim游戏

有若干堆石子,每堆石子的数量都是有限的,合法的移动是“选择一堆石子并拿走若干颗(不能不拿)”,如果轮到某个人时所有的石子堆都已经被拿空了,则判负(因为他此刻没有任何合法的移动)。

Nim游戏结论:所有数字异或和为0,先手必败。

因此,这个问题实际上是让我们计算有多少种情况使得所有正面朝上的数字异或和为0。我们不妨假设S=a1⊕a2⊕...⊕an并且ci=ai⊕bi,假设卡片j1,j2,...jk都是bi朝上,剩下卡片都是ai朝上,那么现在总的异或和就是S⊕cj1⊕cj2⊕...⊕cjk,我们目标就是要找到一些ci的子集,使得他们的异或和为S(因为一个数与自身异或为0),又因为ci=(ci⊕cj)⊕cj,因此我们可以自由通过用ci⊕cj来代替cj(因为ci⊕cj=ci⊕(ci⊕cj)!),因此我们考虑如何简化ci集合:

选择出二进制位某一个位为1的数ci。

使用ci⊕cj来替换所有的ci。

重复上述步骤。

最终,我们会得到包括k个0和n-k个数的集合,下面问题在于如何检测这个集合是不是S的集合?我们可以按数位来讨论,而所有子集的数量一定是2k对于每个n−k个不是零的数字概率就是2k2n,这是先手必败,那么先手必胜就是2n−k−12n−k,因为是减一,所以分数一定是最简的,无需化简!

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