1. 程式人生 > >5301. [CQOI2018]異或序列【莫隊】

5301. [CQOI2018]異或序列【莫隊】

等於 分開 num sum 表示 \n [1] fin 多少

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
不能再這麽頹下去了
昨天打了一天板子感覺現在腦子銹了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],所以讀入的時候左端點要減一。

 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 }

5301. [CQOI2018]異或序列【莫隊】