lixiaotao 2020-01-29
本题是经典的莫队算法
莫队算法是离线查询的一种复杂度优秀的暴力算法。
首先我们需要注意异或的几个性质,相同数字异或等于0,所以我们考虑前缀和,因为从ai-aj的异或值就等于pre[i-1]^pre[j] 前缀和的异或,因为相同部分会抵消。
莫队一般和分块相结合,我们需要维护一个cnt数组,cnt[a[x]]表示异或结果是a[x]的值有多少,这就可以表示贡献度。
几个坑点:
1.异或值可以大于原值,所以首先要注意int,其次数组范围要开大一点
2.为什么在维护add的时候要先算贡献再加,而sub时要先减?
因为有一种特殊情况会使x^k=x,也就是k=0的时候,那么这个时候不能算这个区间,因为这是自身的匹配是空匹配,因为前缀和是pre[i-1]^pre[j],这种情况就是pre[i-1]^pre[i-1]这里面是没有值的。
#include<iostream> #include<algorithm> #include<cstdio> #include<vector> #include<cstring> using namespace std; typedef long long ll; const int N=1<<20; ll ans[N]; ll cnt[N]; ll pos[N]; ll a[N]; ll res; int n,m,k; struct node{ int l,r; int k; }q[N]; bool cmp(node a,node b){ if(pos[a.l]==pos[b.l]) return a.r<b.r; return pos[a.l]<pos[b.l]; } void add(int x){ res+=cnt[a[x]^k]; //贡献 cnt[a[x]]++; } void sub(int x){ cnt[a[x]]--; res-=cnt[a[x]^k]; } int main(){ int i; cin>>n>>m>>k; int block=sqrt(n); for(i=1;i<=n;i++){ cin>>a[i]; a[i]^=a[i-1]; pos[i]=(i-1)/block+1; } for(i=1;i<=m;i++){ cin>>q[i].l>>q[i].r; q[i].k=i; } sort(q+1,q+1+m,cmp); int l=1; int r=0; cnt[0]=1; for(i=1;i<=m;i++){ while(q[i].l<l){ l--; add(l-1); } while(q[i].r>r){ r++; add(r); } while(q[i].l>l){ sub(l-1); l++; } while(q[i].r<r){ sub(r); r--; } ans[q[i].k]=res; } for(i=1;i<=m;i++) cout<<ans[i]<<endl; }