\(\\\)ios
給出一個長爲 \(n\) 的數列 \(A\) 和 \(k\),屢次詢問:c++
對於一個區間 \([L_i,R_i]\),問區間內有多少個不爲空的子段異或和爲 \(k\) 。git
\(\\\)spa
注意到一件有趣的事,就是每次詢問的 \(k\) 相同。code
由於 \(a\oplus a=0\),因此子段異或問題能夠看做前綴異或和的異或,即
\[ a[i]\oplus a[i+1]\oplus...\oplus a[j]=sum[i-1]\oplus sum[j] \]
其中 \(sum[i]=a[1]\oplus a[2]\oplus...\oplus a[i]\) 。ip
那麼問題轉化爲,存在對少對 \(i,j\in[L_i-1,R_i],i!=j\) ,知足
\[ sum[i]\oplus sum[j]=k \]
注意區間問題,由於區間作差的原理是減掉 \(l-1\) 。get
而後能夠注意到,一個值 \(x\) 若想要構成 \(k\) ,其對應的另外一個值是固定的。string
也就是說,咱們的組合方案是肯定的。it
當新加入一個可選值 \(x\) ,咱們的方案數就會 \(+cnt[x^k]\) ,其中 \(cnt[i]\) 表示當前含有可選值 \(i\) 的個數。io
能夠證實,這種計數方式不會算重,由於每一個數字加入時只會計算當前已經有對應的值。
當去掉一個值的時候,方案數 \(-cnt[x^k]\) 便可。
\(\\\)
還有一個問題,就是關於 \(k=0\) 的狀況。
此時每一個值顯然不能計算上本身和本身異或的貢獻。
刪除時固然也要注意不能多減掉本身異或本身的狀況。
只需在 add
和 del
的時候交換一下操做順序便可,具體看代碼。
\(\\\)
忽然失智......Debug 2h 竟只是由於 \(l\) 沒有減 \(1\) ......
還要注意,剛開始 \(0\) 號位置也有一個貢獻。
#include<cmath> #include<cstdio> #include<cctype> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #define N 100010 #define R register #define gc getchar using namespace std; typedef long long ll; inline ll rd(){ ll x=0; bool f=0; char c=gc(); while(!isdigit(c)){if(c=='-')f=1;c=gc();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();} return f?-x:x; } ll n,m,k,ans,bl[N],cnt[1<<18],s[N],res[N]; struct Q{ll l,r,id;}q[N]; inline bool cmp1(Q x,Q y){ return bl[x.l]==bl[y.l]?x.r<y.r:bl[x.l]<bl[y.l]; } inline bool cmp2(Q x,Q y){return x.id<y.id;} inline void del(int p){ --cnt[s[p]]; ans-=cnt[k^s[p]]; } inline void add(int p){ ans+=cnt[k^s[p]]; ++cnt[s[p]]; } int main(){ n=rd(); m=rd(); k=rd(); ll t=sqrt(n); for(R ll i=1;i<=n;++i){ s[i]=s[i-1]^rd(); bl[i]=i/t+1; } for(R ll i=1;i<=m;++i){ q[i].l=rd()-1; q[i].r=rd(); q[i].id=i; } sort(q+1,q+1+m,cmp1); ll nowl=0,nowr=0; cnt[0]=1; for(R ll i=1;i<=m;++i){ while(nowl>q[i].l){--nowl;add(nowl);} while(nowl<q[i].l){del(nowl);++nowl;} while(nowr<q[i].r){++nowr;add(nowr);} while(nowr>q[i].r){del(nowr);--nowr;} res[q[i].id]=ans; } for(R ll i=1;i<=m;++i) printf("%lld\n",res[i]); return 0; }