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

题意:给你n个数字,然后让你求所有满足异或和为0的子集的大小之和。

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

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

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

(1) x能被插入线性基。那么显然x不能在任意一个集合中出现,x的贡献为0。

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

#include<bits/stdc++.h>
#define ll long long
const int inf = 0x3f3f3f3f;
const int N = 1e5+7;
const ll mod = 1e9+7;
using namespace std;
struct Linear_basis{
    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 1;
                }
                x^=b[i];
            }
        }
        return 0;
    }
};
Linear_basis x,y,z;
ll q_pow(ll a,ll n){
    ll ans=1; ll base=a;
    while(n){
        if(n&1) ans=(ans*base)%mod;
        base=(base*base)%mod;
        n>>=1;
    }
    return ans;
}
ll a[N];
vector<ll> v,v1;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    int n;
    while(cin>>n){
        x.init(); y.init(); z.init();
        v.clear(); v1.clear();
        for(int i=1;i<=n;i++) cin>>a[i];
        for(int i=1;i<=n;i++){
            if(!x.insert(a[i])){
                v.push_back(a[i]);
            }else v1.push_back(a[i]);
        }
        ll sum=0;
        sum=(sum%mod+((n-x.cnt)*q_pow(2,n-x.cnt-1))%mod)%mod;
        for(int i=0;i<v.size();i++){
            y.insert(v[i]);
        }
        for(int i=0;i<v1.size();i++){
            z=y;
            for(int j=0;j<v1.size();j++){
                if(i==j) continue;
                z.insert(v1[j]);        
            }
            if(!z.insert(v1[i])){
                sum=(sum+q_pow(2,n-z.cnt-1))%mod;    
            }
        }
        cout<<sum<<endl;
    }
    return 0;
}
原文地址:https://www.cnblogs.com/wmj6/p/11231926.html