1. 程式人生 > >[ CQOI 2018 ] 異或序列

[ CQOI 2018 ] 異或序列

\(\\\)

Description


給出一個長為 \(n\) 的數列 \(A\)\(k\),多次詢問:

對於一個區間 \([L_i,R_i]\),問區間內有多少個不為空的子段異或和為 \(k\)

  • \(n,m,k,A_i\le 10^5\)

\(\\\)

Solution


注意到一件有趣的事,就是每次詢問的 \(k\) 相同。

因為 \(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]\)

那麼問題轉化為,存在對少對 \(i,j\in[L_i-1,R_i],i!=j\) ,滿足
\[ sum[i]\oplus sum[j]=k \]
注意區間問題,因為區間做差的原理是減掉 \(l-1\)

然後可以注意到,一個值 \(x\) 若想要構成 \(k\) ,其對應的另一個值是固定的。

也就是說,我們的組合方案是確定的。

當新加入一個可選值 \(x\) ,我們的方案數就會 \(+cnt[x^k]\) ,其中 \(cnt[i]\) 表示當前含有可選值 \(i\) 的個數。

可以證明,這種計數方式不會算重,因為每個數字加入時只會計算當前已經有對應的值。

當去掉一個值的時候,方案數 \(-cnt[x^k]\)

即可。

\(\\\)

還有一個問題,就是關於 \(k=0\) 的情況。

此時每個值顯然不能計算上自己和自己異或的貢獻。

刪除時當然也要注意不能多減掉自己異或自己的情況。

只需在 adddel 的時候交換一下操作順序即可,具體看程式碼。

\(\\\)

Code


突然失智......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;
}