2019牛客暑期多校训练营(第一场)H XOR

登录—专业IT笔试面试备考平台_牛客网

https://ac.nowcoder.com/acm/contest/881/H

题意:给定n个整数,求满足子集异或和为0的子集大小之和。

题解:相当于求每个数出现在子集中的次数之和。

先对n个数求线性基,设线性基大小为r,可以分别计算线性基内数的贡献和线性基外数的贡献

1.线性基外:共n-r个数,枚举每个数x,将线性基外剩余的n-r-1个数任意排列,显然共有 个集合,这些集合再异或x的结果还是能被线性基异或出,所以x的贡献为

2.线性基内:枚举每个数x,将所有剩余的n-1个数再求一次线性基,设为B,分两种情况:

(1) x不能被B异或出。那么显然x不能在任意一个集合中出现,x的贡献为0。

(2) x可以被B异或出。此时B的大小必定也为r,因为B已经能表示所有n个数了。那么在除去x和B的情况下,剩余n-r-1个数显然也是任意排列,x贡献为

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 100000 + 5;
const int mod = 1e9 + 7;

ll a[maxn];
vector<int> ID;

struct LinerBase {
    int cnt;
    ll b[65];
    void init() {
        cnt = 0;
        memset(b, 0, sizeof(b));
    }
    bool Insert(ll x) {
        for(int i = 63; i >= 0; i--) {
            if(x & (1LL<<i) ) {
                if(!b[i]) { b[i] = x; cnt++; return true; }
                else x ^= b[i];
            }
        }
        return false;
    }
    bool check(ll x) {
        for(int i = 63; i >= 0; i--) {
            if(x & (1LL<<i)) x ^= b[i]; 
        }
        return x==0;
    }
}B1, B2, B3;

ll qpow(ll a, ll b) {
    ll res = 1;
    while(b) {
        if(b & 1) res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}

int main() {
    int n;
    while(~scanf("%d", &n)) {
        for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);
        B1.init(); B2.init(); ID.clear();
        for(int i = 1; i <= n; i++) {
            if(!B1.check(a[i])) {
                B1.Insert(a[i]);
                ID.push_back(i);
            }
            else B2.Insert(a[i]);
        }
        int r = B1.cnt;
        if(r == n) {
            printf("0\n");
            continue;
        }
        ll tp = qpow(2, n-r-1);
        ll ans = (n-r) * tp % mod;
        for(int i = 0; i < ID.size(); i++) {
            ll x = a[ID[i]];
            for(int j = 0; j < 65; j++) B3.b[j] = B2.b[j];
            for(int j = 0; j < ID.size(); j++) {
                if(j != i) B3.Insert(a[ID[j]]);
            }
            if(B3.check(x)) ans = (ans + tp) % mod;
        }
        printf("%lld\n", ans);
    }

    return 0;
}
全部评论

相关推荐

dongsheng66:如果想进大厂的话,在校经历没必要占这么大篇幅,可以把专业技能单独放一个专栏写,可以加个项目经历
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务