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; }