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

Problem Description

∑ S ∈ A , ⨁ x ∈ S x = 0 ∣ S ∣    m o d ( 1 0 9 + 7 ) \sum_{S\in A},\bigoplus_{x\in S}x = 0|S|\;mod(10^9+7) SA,xSx=0Smod(109+7)

He wants to know the sum of sizes for all subsets of A whose xor sum is zero modulo (10^9+7).

中文意思就是求序列 A A A(序列包含 n ( 1 ≤ n ≤ 1 0 5 ) n(1\le n\le 10^5) n(1n105)个元素 a i ( 0 ≤ a i ≤ 1 0 18 ) a_i(0\le a_i\le 10^{18}) ai(0ai1018))中子集元素位异或值为 0 0 0的子集大小之和。

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

A Solution

本人太菜,忘大佬路过指出错误!

首先转换问题,这一步非常重要,他要求子集的大小之和,利用期望的线性性,其实就是要求每个元素出现在合法子集里的方案数。

我们先求出序列的一组线性基,假设插进去了 r r r个元素,那么没有插进去的元素的贡献我们可以立马得到,每个元素的贡献是 2 n − r − 1 2^{n-r-1} 2nr1,加起来就是 r × 2 n − r − 1 r\times 2^{n-r-1} r×2nr1。(按照蔡队的话说就是不在线性基里的元素xjb选

然后再来统计插进线性基内元素的贡献。

我们重新开始,再对其他 n − r n-r nr个数消个元(消元就是求线性基的过程),得到另一组线性基 B B B

枚举这 r r r个元素,先将其他 r − 1 r-1 r1个元素尝试插入上组线性基 B B B得到线性基 B ′ B' B(也就是用另外的 r − 1 + B r-1+B r1+B个数消元),如果我当前枚举的这个元素不能插进线性基 B ′ B' B,则累加贡献 2 n − ∣ B ′ ∣ − 1 2^{n-|B'|-1} 2nB1。因为如果他能插进线性基,就说明这个元素不属于线性基 B ′ B' B的张成 ( s p a n ) (span) (span),那就是线性无关的!

AC_Code

#pragma comment(linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
#define fi first
#define se second
#define endl '\n'
#define o2(x) (x)*(x)
#define BASE_MAX 63
#define mk make_pair
#define eb emplace_back
#define all(x) (x).begin(), (x).end()
#define clr(a,b) memset((a),(b),sizeof((a)))
#define iis std::ios::sync_with_stdio(false); cin.tie(0)
#define my_unique(x) sort(all(x)),x.erase(unique(all(x)),x.end())
using namespace std;
#pragma optimize("-O3")
typedef long long LL;
typedef pair<int, int> pii;
inline LL read(){
    LL x=0;int f=0;char ch=getchar();
    while (ch<'0'||ch>'9') f|=(ch=='-'),ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return x=f?-x:x;
}
inline void write(LL x) {
    if(x==0){putchar('0'),putchar('\n');return;}
    if(x < 0) {putchar('-');x=-x;}
    static char s[23];int l = 0;
    while(x!=0)s[l++]=x%10+48,x/=10;
    while(l)putchar(s[--l]);
    putchar('\n');
}
int lowbit(int x) {return x&(-x);}
template<class T> T big(const T& a1,const T& a2) { return a1>a2?a1:a2; }
template<typename T, typename ...R> T big (const T& f,const R& ...r) { return big(f, big (r...)); }
template<class T> T sml(const T& a1,const T& a2) { return a1<a2?a1:a2; }
template<typename T, typename ...R> T sml (const T& f,const R& ...r) { return sml(f, sml (r...)); }
void debug_out() { cerr << '\n'; }
template<typename T, typename ...R> void debug_out (const T& f,const R& ...r) { cerr << f << " "; debug_out (r...); }
#define debug(...) cerr << "[" << #__VA_ARGS__ << "]: ", debug_out(__VA_ARGS__);

#define print(x) write(x);

const LL INFLL = 0x3f3f3f3f3f3f3f3fLL;
const int HMOD[] = {1000000009, 1004535809};
const LL BASE[] = {1572872831, 1971536491};
const int mod = 998244353 ;
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const int MXN = 1e5 + 7;
int n, m;
LL id[MXN], bs[MXN], two[MXN], bsB[MXN];
int cnt, inBase[MXN];
LL ksm(LL a, int b) {LL res = 1;for (; b; b >>= 1, a = a * a % MOD)if (b & 1)res = res * a % MOD;return res;}
bool insert(LL x, LL *bs) {
    for(int j = BASE_MAX; j >= 0; --j) {//63
        if(!(x >> j)) continue;
        if(bs[j]) x ^= bs[j];
        else {
            bs[j] = x;
            for(int k = j-1; k >= 0; --k) if(bs[k]&&(bs[j]&(1LL<<k))) bs[j]^=bs[k];
            for(int k = j+1; k <= BASE_MAX; ++k) if(bs[k]&(1LL<<j)) bs[k]^=bs[j];
            return true;
        }
    }
    return false;
}
int main() {
#ifndef ONLINE_JUDGE
    freopen("/home/cwolf9/CLionProjects/ccc/in.txt", "r", stdin);
    //freopen("/home/cwolf9/CLionProjects/ccc/out.txt", "w", stdout);
#endif
    two[0] = 1;
    for(int i = 1; i < MXN; ++i) two[i] = two[i-1] * 2 % MOD;
    while(~scanf("%d", &n)) {
        cnt = 0;
        for(int i = 0; i <= BASE_MAX; ++i) bs[i] = 0;
        vector<LL> baseR;
        for(int i = 1; i <= n; ++i) {
            id[i] = read(), inBase[i] = 0;
            if(insert(id[i], bs)) ++ cnt, inBase[i] = 1, baseR.eb(id[i]);
        }
        if(cnt == n) {
            printf("0\n");
            continue;
        }
        LL ans = (LL)(n - cnt)*two[n - cnt - 1]%MOD;
        for(int i = 0; i <= BASE_MAX; ++i) bs[i] = 0;
        int sizB = 0;
        for(int i = 1; i <= n; ++i) {
            if(inBase[i]) continue;
            if(insert(id[i], bs)) ++ sizB;
        }
//        debug(ans)
        int tmp = sizB;
        for(auto x: baseR) {
            memcpy(bsB, bs, sizeof(LL)* 64);
            sizB = tmp;
            for(auto y: baseR) {
                if(x != y && insert(y, bsB)) ++ sizB;
            }
            if(!insert(x, bsB)) ans = (ans + two[n-sizB-1]) % MOD;
//            debug(flag, sizB)
        }
        printf("%lld\n", (ans+MOD)%MOD);
    }
#ifndef ONLINE_JUDGE
    cout << "time cost:" << (double)clock()/CLOCKS_PER_SEC << "ms" << endl;
#endif
    return 0;
}
/*
1
0
3
1 2 3
2
1000000000000000000 1000000000000000000

1
3
2
 */

叉姐的题解

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值