6759: 异或序列
时间限制: 1 Sec 内存限制: 128 MB题目描述
已知一个长度为n的整数数列a1,a2,…,an,给定查询参数l、r,问在al,al+1,…,ar区间内,有多少子序列满足异或和等于k。也就是说,对于所有的x,y(l≤x≤y≤r),满足ax⊕ax+1⊕⋯⊕ay=k的x,y有多少组。
输入
输入第一行为3个整数n,m,k。第二行为空格分开的n个整数,即a1,a2,…,an。接下来m行,每行两个整数lj,rj,代表一次查询。
输出
输出共m行,对应每个查询的计算结果。
样例输入
4 5 1
1 2 3 1
1 4
1 3
2 3
2 4
4 4
样例输出
4
2
1
2
1
提示
对于30%的数据,1≤n,m≤1000。
对于100%的数据,1≤n,m≤105,0≤k,ai≤105,1≤lj≤rj≤n。
来源/分类
看到多次区间查询,就想到了莫队算法,但是莫队中对于区间状态转移的处理却是需要斟酌。
题目已经很明显的提示了,异或异或,也许我们应该使用异或前缀和,(orz)
对于异或前缀和 我们知道 i and j 之间的异或和 == sum[j]^sum[i-1]
那么对于l 如果小于 Q.l-1,那么我们就先清除这位置的影响,然后下标++
如果大于Q.l-1,那就下标--,再加上这位置影响
我们要让求异或为k的组合数
sum[j] ^ sum[i] = k 即 sum[j] ^ k = sum[i]
用cnt 记录 区间内前缀和的数量变化
那么ans += val * (cnt[sum[j] ^ k])即这个点所造成的k组数影响
1 #include<iostream> 2 #include<cstdio> 3 #include<math.h> 4 #include<algorithm> 5 using namespace std; 6 7 const int maxn = 1e5+5; 8 int n,m,k; 9 int block; 10 int sum[maxn]; 11 int ans; 12 int tmp; 13 int cnt[maxn]; 14 15 struct node 16 { 17 int l,r,id,ans; 18 friend bool operator < (const node &a,const node&b) 19 { 20 if(a.l / block != b.l / block)return a.l < b.l; 21 return a.r < b.r; 22 } 23 node(){} 24 node(int l,int r,int id):l(l),r(r),id(id){} 25 }Q[maxn]; 26 27 bool cmp(node a,node b) 28 { 29 return a.id < b.id; 30 } 31 32 void revise(int x,int val) 33 { 34 cnt[sum[x]]+=val; 35 ans += val*cnt[sum[x]^k]; 36 } 37 int main() 38 { 39 scanf("%d%d%d",&n,&m,&k); 40 block = sqrt(n); 41 for(int i=1;i<=n;i++) 42 { 43 scanf("%d",&tmp); 44 sum[i] = tmp ^ sum[i-1]; 45 } 46 for(int i=1;i<=m;i++) 47 { 48 scanf("%d%d",&Q[i].l,&Q[i].r); 49 Q[i].id = i; 50 } 51 sort(Q+1,Q+m+1); 52 int l = 1,r = 0; 53 ans = 0; 54 for(int i=1;i<=m;i++) 55 { 56 while(l < Q[i].l - 1)revise(l,-1),l++; 57 while(l > Q[i].l - 1) l--,revise(l,1); 58 while(r < Q[i].r)r++,revise(r,1); 59 while(r > Q[i].r)revise(r,-1),r--; 60 Q[i].ans = ans; 61 } 62 sort(Q+1,Q+m+1,cmp); 63 for(int i=1;i<=m;i++)printf("%d ",Q[i].ans); 64 return 0; 65 }