Codeforces - 662A Gambling Nim

题意:

  n张卡牌,正反两面有两个数字。每一面的概率都为0.5。将所有卡片的值异或起来,求异或值不为0的概率。

题解:

  考虑异或值为0的情况。

  用sum表示a[1]^...^a[n]的值。用c[i]表示a[i]^b[i]。那么sum^c[i]^...^c[j]代表总的异或值。即sum=c[i]^...^c[j]时,他们的异或值为0。

  那么问题就变成求c的子集,使得子集内的所有值异或和为sum。

  求出c[1]~c[n]的线性基,用bit数组表示。

  对于sum,若他不能用线性基表示,即总的异或和恒不为0,答案就为1/1。

  如果能用线性基表示,所有异或和为sum的情况数就为2n-tot(tot为线性基的大小)。异或和为0的概率为2n-tot/2n = 1/2tot

  那么异或和不为0的概率为(2tot-1)/2tot

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e5+10;
int n;
int tot;
ll b, sum;
ll a, bit[65];
void add(ll *a, ll b) {
    for(int i = 62; i >= 0; i--) if((1ll<<i)&b) {
        if(a[i]) b ^= a[i];
        else {
            a[i] = b; 
            tot++;
            return ;
        }
    }
}
int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) {
        scanf("%lld%lld", &a, &b);
        sum ^= a;
        add(bit, a^b);
    }
    int cnt;
    for(int i = 62; i >= 0; i--) if((1ll<<i)&sum) {
        sum ^= bit[i];
        cnt++;
    }
    if(sum) {
        puts("1/1");
        return 0;
    }
    ll ans = 1ll<<tot;
    printf("%lld/%lld\n", ans-1, ans);
} 
View Code

 

posted @ 2018-05-25 22:42  Pneuis  阅读(241)  评论(3编辑  收藏  举报