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 ,因爲是減一,所以分數一定是最簡的,無需化簡!- 選擇出二進制位某一個位爲1的數
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);
}
}