異或序列
題目描述
已知一個長度為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。
首先異或和滿足字首,也就是說設val[i]為a[1]^a[2]^...^a[i],那麼a[i]^a[i+1]^...^a[j]=val[j]^val[i-1]
而且異或不僅滿足交換律,而且對於a^b=c時,a^c=b,b^c=a這兩個式子同樣成立
那麼就好做了,假設當前i到j這個子串的異或和為k,就說明val[j]^val[i-1]=k,也就是val[i-1]^k=val[j],val[j]^k=val[i-1]
然後在區間轉移的時候,設flagi]為當前區間值為i的字首有多少個,然後對於增加序列長度的操作,假設新加的位置為r+1,我們先將flag[val[r+1]]++,然後求出ans+=flag[val[r+1]^k],左邊擴充套件也是如此,不過注意,向左擴充套件時,對ans的更新是用val[l-1]的,因為是val[j]與val[i-1]可以滿足字首
而且向右擴充套件的時候,如果val[r+1]^k=val[l-1]的話,ans++,因為我們更新的時候沒有計算[l...r+1]區間的影響,所以要維護一下
而對於區間縮小的情況,就ans先減,再更新flag,因為要先消除貢獻再減flag,其它步驟類似就好了
#include <iostream> #include<bits/stdc++.h> using namespace std; typedef long long ll; int n,m,k,block; int val[100050],part[100050]; int ans,flag[100050]; struct node { int l,r,id; int ans; }query[100050]; bool cmp1(node x,node y) { if(part[x.l]==part[y.l]) { return x.r<y.r; } return x.l<y.l; } bool cmp2(node x,node y) { return x.id<y.id; } void update(int pos,int op) { if(!op) { flag[val[pos]]--; ans-=flag[val[pos]^k]; } else { ans+=flag[val[pos]^k]; flag[val[pos]]++; } } void fun() { int i,l=1,r=0; flag[0]=1; for(i=1;i<=m;i++) { while(r<query[i].r) { update(++r,1); } while(r>query[i].r) { update(r--,0); } while(l<query[i].l) { update(l-1,0); l++; } while(l>query[i].l) { l--; update(l-1,1); } query[i].ans=ans; } } int main() { scanf("%d %d %d",&n,&m,&k); block=sqrt(n); for(int i=1;i<=n;i++) { scanf("%d",&val[i]); val[i]^=val[i-1]; part[i]=(i-1)/block; } for(int i=1;i<=m;i++) { scanf("%d %d",&query[i].l,&query[i].r); query[i].id=i; } sort(query+1,query+1+m,cmp1); fun(); sort(query+1,query+1+m,cmp2); for(int i=1;i<=m;i++) { printf("%d\n",query[i].ans); } return 0; }