牛客多校1-H XOR(线性基)

题目链接

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

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

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

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

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

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

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

//#pragma comment(linker, "/STACK:102400000,102400000")
#include "bits/stdc++.h"
#define pb push_back
#define ls l,m,now<<1
#define rs m+1,r,now<<1|1
#define hhh printf("hhh\n")
#define see(x) (cerr<<(#x)<<'='<<(x)<<endl)
using namespace std;
typedef long long ll;
typedef pair<int,int> pr;
inline int read() {int x=0;char c=getchar();while(c<'0'||c>'9')c=getchar();while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();return x;}

const int maxn = 1e5+10;
const int mod = 1e9+7;
const double eps = 1e-9;

ll a[maxn];
vector<int> ID; //用ID来存储哪些数能插入到线性基中

struct LinerBase{
    int cnt;
    ll b[64];
    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)!=EOF) {
        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.Insert(a[i])) ID.push_back(i);
            else B2.Insert(a[i]);
        int r=B1.cnt;
        if(r==n) { printf("0\n"); continue; } //全都插入线性基了,表明所有的数互相异或无法构造出0
        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<64; ++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);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值