2019暑假牛客多校赛第一场H.XOR(线性基)

H.XOR(线性基)

传送门
题目描述
Bobo has a set A of n integers a 1 , a 2 , … , a n a_1, a_2, \dots, a_n a1,a2,,an .He wants to know the sum of sizes for all subsets of A whose xor sum is zero modulo ( 1 0 9 + 7 ) (10^9+7) (109+7)Formally, find ( ∑ S ⊆ A , ⊕ x ∈ S x = 0 ∣ S ∣ )   m o d   ( 1 0 9 + 7 ) \left(\sum_{S \subseteq A, \oplus_{x \in S} x = 0} |S|\right) \bmod (10^9+7) (SA,xSx=0S)mod(109+7).Note that ⊕ \oplus denotes the exclusive-or (XOR).
输入描述:
The input consists of several test cases and is terminated by end-of-file.

The first line of each test case contains an integer n.
The second line contains n integers a 1 , a 2 , … , a n a_1, a_2, \dots, a_n a1,a2,,an
1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1n105
0 ≤ a i ≤ 1 0 18 0 \leq a_i \leq 10^{18} 0ai1018
The number of test cases does not exceed 100.
The sum of n does not exceed 2 × 1 0 6 2 \times 10^6 2×106
输出描述:
For each test case, print an integer which denotes the result.
示例1
输入

1
0
3
1 2 3
2
1000000000000000000 1000000000000000000
输出
1
3
2

题目大意:
给定一个序列S,问这个序列的所有子序列中,子序列所有的元素的异或和为0的子序列长度和为多少?可以转化问题,求每个元素属于子集数的和,也就是统计每个元素对答案的贡献。

思路:

首先,很明显的异或,用线性基。

线性基有几个性质
1、基内一定是线性无关的。
2、集合B是S的线性基,则S中的所有元素都可有B中元素异或得到,且只有一种异或方式。
3、某集合S的可以得到长度为r的线性基B(r < n),则如果基中元素可以由另外(n - 1)个元素异或得到,那么会存在另一个不同的长度为r的线性基.

所以这题,我们可以先构建一个线性基,里面含有cnt个元素。
那么其余的 N − c n t N-cnt Ncnt个元素中每个都可以构成 2 N − c n t − 1 2^{N-cnt-1} 2Ncnt1个子串,所以每个的贡献度为 2 N − c n t − 1 2^{N-cnt-1} 2Ncnt1(选一个,然后其他的N-cnt-1随便取)因为是线性基是极大线性无关组,所以N-cnt-1都可以由线性基里的特定元素集表示。

然后我们就看线性集里面元素的贡献度。先将N - cnt 个数求出一个线性基,再将原来基中的cnt-1个数再放入当前的线性基中,剩下的一个数判断它是否能由新得到的基组成, 如果可以那么他的贡献度还是 2 N − c n t − 1 2^{N - cnt- 1} 2Ncnt1,因为当前的基的长度还是cnt , 还有 n - r个数可以和他组合.(可以构成的话还是极大线性无关组,cnt相同)(性质三)

ac_code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define LL long long
int n;
LL a[110000];
LL bssr[110],bssb[110],bssd[110];
LL ans=0,mod=1000000007;
bool flg[110000];
LL q[110000],hd=0;
LL cnt=0;

bool add(LL x,LL y[]){    //线性基的插入
    for(int i=63;i>=0;--i)
        if((LL)1<<i&x){
        if(!y[i]){
            y[i]=x;
            ++cnt;
            return true;
        }
        x^=y[i];
    }
    return false;
}

bool chck(LL x,LL y[]){     //判断是否可以由线性基构成
    for(int i=63;i>=0;--i)
        if((LL)1<<i&x)
        x^=y[i];
    return !x;
}

LL power(LL x,int y){   //快速幂
    LL ans=1;
    while(y){
        if(y&1) ans=(ans*x)%mod;
        x=(x*x)%mod;
        y>>=1;
    }
    return ans;
}

void init(){       //初始化
    memset(bssr,0,sizeof(bssr));
    memset(bssb,0,sizeof(bssb));
    for(int i=1;i<=n;++i)
        flg[i]=false;
    ans=0;
    hd=0;
    cnt=0;
}

int main(){
    while(~scanf("%d",&n)){
        init();
        for(int i=1;i<=n;++i)  scanf("%lld",&a[i]);
        for(int i=1;i<=n;++i){
            flg[i]=add(a[i],bssr);
            if(flg[i]) q[++hd]=a[i];
        }
        if(cnt==n){
            printf("0\n");
            continue;
        }
        ans+=(n-cnt)*power(2,n-cnt-1)%mod; //N-cnt个元素的贡献

        for(int i=1;i<=n;++i)
            if(!flg[i])    //cnt以外的元素构成的线性基
            add(a[i],bssb);
        for(int k=1;k<=hd;++k){    
            memset(bssd,0,sizeof(bssd));
            cnt=0;
            for(int i=1;i<=hd;++i)
                if(i!=k)
                add(q[i],bssd);
            for(int i=0;i<=63;++i)
                if(bssb[i])
                add(bssb[i],bssd); //n-1的线性基
            if(chck(q[k],bssd))    //判断是否可以由线性基得到
                ans=(ans+power(2,n-cnt-1))%mod;
        }
        printf("%lld\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值