题意: 给定n个数A[1..n](1<=n<=10^6,0<=A[i]<=10^9),和k (1<=k<=10^9)
求有多少个区间[L,R]使得A[L] xor A[L+1] xor … xor A[R-1] xor A[R] >= k。
首先,令D[ T ] = 0 xor A[1]xor A[2] xor …xor A[ T ]。
题目转化为求有多少 0<= s < t <= n 使得 D[s]^D[t] >= k。
for(int i=1;i<=n;++i){
将D[i-1]加入某种数据结构
在结构中搜索有多少个数在xor D[i]之后 >= k。
}
这题使用的是trie树,从根开始,每一层依次是第29位,28位到第0位的判断,左0右1的方式存入trie树。
一个数字的值,取决于它的位置,所以其实树中没有实际上存下每个数的值。
由于要求的是异或了D[i]之后的值,所以在trie树中搜索的时候要注意如果D[i]的那一位为1,则要把树当做左1右0来看待。
为了统计答案,每个节点要记录这个节点以下有多少个数。
这里用数组模拟了树形结构。
小问题:trie树需要多少个节点? —- 2^24个
虽然树高30(根的高度算0),但是其实不需要2^31这么多的节点(根本存不下)。
首先把100万个数当做2^20.于是最坏情况是这2^20个数在第20层已经全部分开了。
需要的节点个数 = 前20层 + 后10层。
前20层:一共也才2^21节点
后10层:2^20个数,每个数每层有一个节点,一共2^20 * 10
所以一共是12* (2^20) = 3 * 2^22 < 2^24
所以节点数一定不超过2^24。
/*
1356 ms 207000 KB
*/
#include <iostream>
#include <cstdio>
#include <cmath>
#define out(i) <<#i<<"="<<(i)<<" "
#define OUT1(a1) cout out(a1) <<endl
#define OUT2(a1,a2) cout out(a1) out(a2) <<endl
#define OUT3(a1,a2,a3) cout out(a1) out(a2) out(a3)<<endl
#define maxn 1000007
#define LL long long
using namespace std;
int n,k;
int A[maxn],D[maxn],H[31];
int L[1<<24],R[1<<24],S[1<<24],P,T;
void Init(){
L[0]=R[0]=S[0]=P=T=0;
}
void Insert(int &rt,int depth,int value){
if(!rt){
rt=++P;
L[rt]=R[rt]=S[rt]=0;
}
++S[rt];
if(depth==30) return;
if(value&H[depth]) Insert(R[rt],depth+1,value);
else Insert(L[rt],depth+1,value);
}
LL Query(int &rt,int depth,int value){
if(!rt) return 0;
if(depth==30) return S[rt];
if(value&H[depth]){//左1右0
if(k&H[depth]) return Query(L[rt],depth+1,value);
else return S[L[rt]]+Query(R[rt],depth+1,value);
}
else{//左0右1
if(k&H[depth]) return Query(R[rt],depth+1,value);
else return S[R[rt]]+Query(L[rt],depth+1,value);
}
}
int main(void)
{
H[29]=1;
for(int i=28;i>=0;--i) H[i]=H[i+1]<<1;
while(~scanf("%d%d",&n,&k)){
for(int i=1;i<=n;++i) scanf("%d",&A[i]);
for(int i=1;i<=n;++i) D[i]=D[i-1]^A[i];
Init();
LL ANS=0;
for(int i=1;i<=n;++i){
Insert(T,0,D[i-1]);
ANS+=Query(T,0,D[i]);
}
printf("%I64d\n",ANS);
}
return 0;
}