Codeforces617E【莫隊演算法+字首異或】
阿新 • • 發佈:2018-12-24
題意:
給出一系列數,對每個查詢區間,計算有多少個子區間異或為k。
思路:
可以先預處理異或字首,一個區間[L,R]的異或值=sum[R]^sum[L-1];
如果當前區間是[a,b],加一個右端點b+1,那麼這個b+1的貢獻就是[a,b]區間內有多少個sum[x]=sum[b+1]^k
那麼我們可以每次記錄num[sum[x]]即num[sum[b+1]^k],並記錄num[sum[b+1]]++,同理左區間。
給出一系列數,對每個查詢區間,計算有多少個子區間異或為k。
思路:
可以先預處理異或字首,一個區間[L,R]的異或值=sum[R]^sum[L-1];
如果當前區間是[a,b],加一個右端點b+1,那麼這個b+1的貢獻就是[a,b]區間內有多少個sum[x]=sum[b+1]^k
那麼我們可以每次記錄num[sum[x]]即num[sum[b+1]^k],並記錄num[sum[b+1]]++,同理左區間。
那麼我們就可以使用莫隊演算法。
#include <bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> PII; const int N=2e6+10; int sum[N],pos[100010]; LL num[N]; int n,m,k,x; struct asd{ int left,right,id; LL res; }e[100010]; bool cmp(asd x,asd y) { if(pos[x.left]==pos[y.left]) return x.right<y.right; return pos[x.left]<pos[y.left]; } LL ans; //答案遵守ans先加,再變;先變,ans再減; void solve() { sort(e,e+m,cmp); ans=0; memset(num,0,sizeof(num)); int L=0,R=0; for(int i=0;i<m;i++) { while(L<e[i].left-1) //當他在區間左邊,他要減去他產生右端 { num[sum[L]]--; //先變 ans-=num[sum[L]^k]; //答案再減 L++; } while(L>=e[i].left) //當他在區間右邊,他要加上他右端 { L--; ans+=num[sum[L]^k]; //先加答案 num[sum[L]]++; //再變 } while(R<=e[i].right) //小於,要加左邊 { ans+=num[sum[R]^k]; //先加答案 num[sum[R]]++; //再變 R++; } while(R>e[i].right+1) //大,要減左 { R--; num[sum[R]]--; //先變 ans-=num[sum[R]^k]; //再減答案 } e[e[i].id].res=ans; } for(int i=0;i<m;i++) printf("%lld\n",e[i].res); } int main() { scanf("%d%d%d",&n,&m,&k); sum[0]=0; int block=(int)sqrt(n); for(int i=1;i<=n;i++) { scanf("%d",&x); sum[i]=sum[i-1]^x; pos[i]=(i-1)/block+1; } for(int i=0;i<m;i++) { scanf("%d%d",&e[i].left,&e[i].right); e[i].id=i; } solve(); return 0; } /* 6 2 3 1 2 1 1 0 3 1 6 3 5 5 3 1 1 1 1 1 1 1 5 2 4 1 3 */