Codeforces - 662A. Gambling Nim - 博弈

Gambling Nim

題目鏈接

分類bitmask math matrices probabilities

1.題意概述

  • 給你n(1n500000) 張卡片,每張卡片的兩個面(正面ai 反面bi )都有寫數字,每個面都有0.5的概率正面,卡牌正反面的概率相互獨立,求把所有卡牌正面數字(ci=ai or bi )拿來玩Nim遊戲,先手必勝的概率。

2.解題思路

  • 什麼是Nim遊戲

    有若干堆石子,每堆石子的數量都是有限的,合法的移動是“選擇一堆石子並拿走若干顆(不能不拿)”,如果輪到某個人時所有的石子堆都已經被拿空了,則判負(因爲他此刻沒有任何合法的移動)。

  • Nim遊戲結論:所有數字異或和爲0,先手必敗。

  • 因此,這個問題實際上是讓我們計算有多少種情況使得所有正面朝上的數字異或和爲0。我們不妨假設S=a1a2...an 並且ci=aibi ,假設卡片j1,j2,...jk 都是bi 朝上,剩下卡片都是ai 朝上,那麼現在總的異或和就是Scj1cj2...cjk ,我們目標就是要找到一些ci 的子集,使得他們的異或和爲S (因爲一個數與自身異或爲0),又因爲ci=(cicj)cj ,因此我們可以自由通過用cicj 來代替cj (因爲cicj=ci(cicj) !),因此我們考慮如何簡化ci 集合:

    1. 選擇出二進制位某一個位爲1的數ci
    2. 使用cicj 來替換所有的ci
    3. 重複上述步驟。

    最終,我們會得到包括k個0和n-k個數的集合,下面問題在於如何檢測這個集合是不是S 的集合?我們可以按數位來討論,而所有子集的數量一定是2k 對於每個nk 個不是零的數字概率就是2k2n ,這是先手必敗,那麼先手必勝就是2nk12nk ,因爲是減一,所以分數一定是最簡的,無需化簡!

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);
    }
}
發佈了350 篇原創文章 · 獲贊 47 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章