题意:给出一个序列,问有多少个子序列的异或的值大于等于k。
思路:首先预处理出一个异或的前缀值,对于一个位置j,我们的目标是找到所有的I<j,使得s[j]^s[I]>=k
如果一个一个的求异或值,复杂度为O(n^2),现在考虑一位一位的比较
将前缀的异或值的二进制字符串插入到trie树中,结点的权值为以这个前缀开头的字符串数量
记录当前已走过的二进制位的和为sum,当前二进制位为b,对于当前深度I,如果sum+(1<<i)>=k,那么就加上b^1的权值然后继续朝着b子树向下走
否则的话更新sum,然后想b^1这颗子树走
这样做的话复杂度为O(n*31).
#include <bits/stdc++.h>
#define eps 1e-6
#define LL long long
#define pii pair<int, int>
#define pb push_back
#define mp make_pair
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
const int maxnode = 20000000;
const int N = 30;
int n, k;
int s[1000100];
int ch[maxnode][2];
int val[maxnode];
int sz;
void add(int num) {
int u = 0;
for (int i = N; i >= 0; i--) {
int c = (num>>i) & 1;
if (!ch[u][c])
ch[u][c] = sz++;
val[ch[u][c]]++;
u = ch[u][c];
}
}
int cal(int num) {
int ans = 0, sum = 0, u = 0;
for (int i = N; i >= 0; i--) {
int c = (num>>i) & 1;
if (sum+(1<<i) >= k) {
ans += val[ch[u][c^1]];
//cout << val[ch[u][c^1]] << " " << c << endl;
u = ch[u][c];
if (!u) break;
}
else {
sum += 1<<i;
u = ch[u][c^1];
if (!u) break;
}
}
//cout << sum << endl;
return ans;
}
int main()
{
//freopen("input.txt", "r", stdin);
scanf("%d%d", &n, &k);
LL ans = 0;
sz = 1;
add(0);
for (int i = 1; i <= n; i++) {
int a;
scanf("%d", &a);
s[i] = s[i-1] ^ a;
ans += cal(s[i]);
add(s[i]);
//cout << ans << endl;
}
cout << ans;
return 0;
}