BZOJ5301:[CQOI2018]异或序列(莫队)

Description

已知一个长度为 n 的整数数列 a[1],a[2],…,a[n] ,给定查询参数 l、r ,问在 [l,r] 区间内,有多少连续子
序列满足异或和等于 k 。
也就是说,对于所有的 x,y (l≤x≤y≤r),能够满足a[x]^a[x+1]^…^a[y]=k的x,y有多少组。

Input

输入文件第一行,为3个整数n,m,k。
第二行为空格分开的n个整数,即ai,a2,….an。
接下来m行,每行两个整数lj,rj,表示一次查询。
1≤n,m≤105,O≤k,ai≤105,1≤lj≤rj≤n

Output

输出文件共m行,对应每个查询的计算结果。

Sample Input

4 5 1
1 2 3 1
1 4
1 3
2 3
2 4
4 4

Sample Output

4
2
1
2
1

Solution

不能再这么颓下去了
昨天打了一天板子感觉现在脑子锈了QAQ……
题意杀了好久……明明子序列应该是不连续的
一看题目这个形式基本莫队没跑了,
然而如果两端加入/减掉一个数,这个数对于区间的影响是靠近它的连续一段
这显然是没法维护的。所以可以维护一个异或前缀和sum
因为a[x] xor a[x+1]……xor a[y-1] xor a[y] 等价于 sum[y] xor sum[x-1]
那么我们就可以用莫队来维护前缀和了。
每次往莫队里加入/删除前缀和的时候只需要考虑有多少个数与当前数异或等于k,开个桶即可。
因为查询区间[x,y]的时候我们需要sum[x-1]~sum[y],所以读入的时候左端点要减一。

Code

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<cmath>
 5 #include<algorithm>
 6 #define N (500000+100)
 7 using namespace std;
 8 
 9 struct node{int x,y,num,id,ans;}ask[N];
10 int Keg[N],sum[N],n,m,k,x,now;
11 
12 bool cmp1(node a,node b){return a.id==b.id?a.y<b.y:a.id<b.id;}
13 bool cmp2(node a,node b){return a.num<b.num;}
14 
15 void Ins(int pos){now+=Keg[sum[pos]^k];    Keg[sum[pos]]++;}
16 void Del(int pos){Keg[sum[pos]]--; now-=Keg[sum[pos]^k];}
17 
18 int main()
19 {
20     scanf("%d%d%d",&n,&m,&k);
21     int unit=sqrt(n);
22     for (int i=1; i<=n; ++i)
23         scanf("%d",&x),sum[i]=sum[i-1]^x;
24     for (int i=1; i<=m; ++i)
25     {
26         scanf("%d%d",&ask[i].x,&ask[i].y);
27         ask[i].x--; ask[i].num=i;
28          ask[i].id=ask[i].x/unit;
29     }
30     sort(ask+1,ask+m+1,cmp1);
31     
32     int l=1,r=0;
33     for (int i=1; i<=m; ++i)
34     {
35         while (l<ask[i].x) Del(l++);
36         while (l>ask[i].x) Ins(--l);
37         while (r<ask[i].y) Ins(++r);
38         while (r>ask[i].y) Del(r--);
39         ask[i].ans=now;
40     }
41     
42     sort(ask+1,ask+m+1,cmp2);
43     for (int i=1; i<=m; ++i)
44         printf("%d\n",ask[i].ans);
45 }
posted @ 2018-04-24 15:50  Refun  阅读(167)  评论(0编辑  收藏  举报