1. 程式人生 > >bzoj5301[CQOI2018]異或序列

bzoj5301[CQOI2018]異或序列

pre std 兩個 scan int scanf init 我們 pri

題意

已知一個長度為 n 的整數數列 a[1],a[2],…,a[n] ,給定查詢參數 l、r ,問在 [l,r] 區間內,有多少連續子
序列滿足異或和等於 k 。
也就是說,對於所有的 x,y (l≤x≤y≤r),能夠滿足a[x]^a[x+1]^…^a[y]=k的x,y有多少組。

分析

這樣的題目首先按照異或運算前綴和,就變成多次查詢區間內有多少對數滿足異或和為k.
考慮簡單的情況.異或和為0的時候,就變成查詢區間內有多少對數相同.這是很顯然的莫隊題目.
那麽異或和為k的時候我們也可以考慮莫隊.比較簡明的思路是用trie樹維護區間內所有的數字,加入/刪除數字的時候更新答案.
另一種方式是,把所有的前綴和異或上k,組成另外一個數列,那麽對於詢問的區間在原先的前綴和數列和異或k的前綴和數列上都有一個數集,然後找兩個數集的相同的數對(就是找這樣的數對:數值相同,但是一個數在這個數集,另一個數在那個數集).

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=100005;
int a[maxn],s1[maxn],s2[maxn];
int cnt1[500000],cnt2[500000],ans;
int SZ=250;
struct query{
    int l,r,num,ans;
    void read(){
        scanf("%d%d",&l,&r);l--;
    }
}Q[maxn];
bool cmp1(const query &A,const query &B){
    if(A.l/SZ!=B.l/SZ)return A.l<B.l;
    return A.r<B.r;
}
void init(int l,int r){
    //printf("%d %d\n",l,r);
    
    for(int i=l;i<=r;++i){
        //printf("%d ",s1[i]);
        cnt1[s1[i]]++;
    }//printf("\n");
    for(int i=l;i<=r;++i){
        cnt2[s2[i]]++;//printf("%d ",s2[i]);
        ans+=cnt1[s2[i]];
    }//printf("\n");
    //printf("%d\n",ans);
}
void move(int l1,int r1,int l2,int r2){
    for(int i=l1;i<l2;++i){
        ans-=cnt2[s1[i]];ans-=cnt1[s2[i]];
        cnt1[s1[i]]--;cnt2[s2[i]]--;
        if(s1[i]==s2[i])ans+=1;
    }
    for(int i=l1-1;i>=l2;--i){
        ans+=cnt2[s1[i]];ans+=cnt1[s2[i]];
        cnt1[s1[i]]++;cnt2[s2[i]]++;
        if(s1[i]==s2[i])ans-=1;
    }
    for(int i=r1+1;i<=r2;++i){
        ans+=cnt2[s1[i]];ans+=cnt1[s2[i]];
        cnt1[s1[i]]++;cnt2[s2[i]]++;
        if(s1[i]==s2[i])ans-=1;    
    }
    for(int i=r1;i>r2;--i){
        ans-=cnt2[s1[i]];ans-=cnt1[s2[i]];
        cnt1[s1[i]]--;cnt2[s2[i]]--;
        if(s1[i]==s2[i])ans+=1;
    }
}
int res[maxn];
int main(){
    int n,m,k;scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;++i){
        scanf("%d",a+i);
    }
    for(int i=1;i<=n;++i)s1[i]=s1[i-1]^a[i];
    for(int i=0;i<=n;++i)s2[i]=s1[i]^k;
    for(int i=1;i<=m;++i)Q[i].read();
    for(int i=1;i<=m;++i)Q[i].num=i;
    sort(Q+1,Q+m+1,cmp1);
    init(Q[1].l,Q[1].r);Q[1].ans=ans;
    for(int i=2;i<=m;++i){
        move(Q[i-1].l,Q[i-1].r,Q[i].l,Q[i].r);
        Q[i].ans=ans;
    }
    if(k==0){
        for(int i=1;i<=m;++i)Q[i].ans-=(Q[i].r-Q[i].l+1);
    }
    for(int i=1;i<=m;++i)Q[i].ans/=2;
    for(int i=1;i<=m;++i)res[Q[i].num]=Q[i].ans;
    for(int i=1;i<=m;++i)printf("%d\n",res[i]);
    return 0;
}

bzoj5301[CQOI2018]異或序列