bzoj2844

http://www.lydsy.com/JudgeOnline/problem.php?id=2844

线性基。。。

先把线性基搞出来,然后不断逼近答案,如果这个基比答案小了,那么说明要加上,同时加上贡献:现在的位i +1<<(now-i) 为什么呢,我是这样理解的:一个数分两种情况:选这位和不选这位,如果前面选的位和当前q不同的话,那么前面已经统计过答案了,每次统计答案都是当已经选的数是q的一个子集。我们统计的答案是不选这个位,不选的话,肯定q小,那么这样的数有1<<(now-i)个,因为后面有now-i个基。

然后还要+1,因为我们只统计了比这个数小的数的个数。还要乘上2^(n-now),因为前面now个数已经足够构成基底,那么后面n-now个数肯定会重复。这样会复制2^(n-now)个数。

还有一种解释方法:因为后面都被消成0了,所以选不选都没关系,又因为后面每个a的组合都不一样,和前面搭配都不一样,所以可以。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100010, mod = 10086;
int n, now;
ll q;
ll a[N], bin[40];
void gauss()
{
    now = 1;
    for(int i = 30; i >= 0; --i)
    {
        int x = now;
        while(x <= n && !(a[x] & bin[i])) ++x; //没有这位
        if(x == n + 1) continue;
        swap(a[now], a[x]); // 消去这位其他的1
        for(int j = 1; j <= n; ++j) if(j != now && a[j] & bin[i]) a[j] ^= a[now]; 
        ++now;
    }
    --now;
}
int main()
{
    bin[0] = 1; for(int i = 1; i <= 30; ++i) bin[i] = bin[i - 1] * 2;
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    scanf("%d", &q);
    gauss();
    ll ans = 0, val = 0;
    for(int i = 1; i <= now; ++i) if((val ^ a[i]) <= q) 
    {
        val ^= a[i];
        ans += bin[now - i] % mod;
    }
    for(int i = 1; i <= n - now; ++i) ans = (ans << 1) % mod;
    ++ans;
    printf("%lld\n", (ans % mod + mod) % mod); 
    return 0;
}
View Code

 

posted @ 2017-05-10 15:13  19992147  阅读(231)  评论(0编辑  收藏  举报