617.E . XOR and Favorite Number 莫隊+異或前綴和

題目鏈接: https://codeforces.com/contest/617/problem/E

題意:

長爲 1e51e5 的數組 aa ,和 1e51e5 個查詢,每次查詢要求出區間 [li,ri][l_i,r_i] 中有多少 個子區間使得該區間內的異或和爲 kk

做法:

因爲我們要快速求得一個區間 [li,ri][l_i,r_i] 中的異或和,所以我們大可以先把所有的值 a[i]a[i] 變成 a[1]a[1] ^ a[2]...a[i]a[2]...a[i] 這樣我們就可以在 O(1)O(1) 的時間裏得到區間的和,即 [li,ri]=ar[l_i,r_i]=a_r ^ al1a_{l-1}

然後每次對邊界進行加減就變得很快,莫隊的思想就出現了。

比如我們要將右區間變大,那麼對於原來的區間 [l,r1][l,r-1] 中的影響會先保存在數組 numnum 裏,若加入 rr 之後存在 xx 個區間 xor[li,r]=kxor[l_i,r]=k ,那麼一定有 num[arnum[a_r ^ k]=xk]=x ,因爲 ala_l 是前綴異或和, ala_l ^ ara_r 其實就是區間 [l+1,r][l+1,r] 的異或和,所以是可以直接表示的。

代碼

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=100005;
const int MAX=(1<<20)+5;

ll num[MAX],ans[maxn],tmp;
int n,m,k,a[maxn];
struct node{
    int l,r,id,blo;
}e[maxn];
bool cmp(node a,node b){
    if(a.blo==b.blo) return a.r<b.r;
    return a.blo<b.blo;
}
void add(int p){
    tmp+=num[a[p]^k];
    num[a[p]]++;
}
void del(int p){
    num[a[p]]--;
    tmp-=num[a[p]^k];
}
int main(){
    scanf("%d%d%d",&n,&m,&k);
    int sz=sqrt(n+1);
    rep(i,1,n){
        scanf("%d",&a[i]);
        a[i]^=a[i-1];
    }
    rep(i,1,m){
        scanf("%d%d",&e[i].l,&e[i].r);
        e[i].id=i; e[i].blo=e[i].l/sz;
    }
    sort(e+1,e+1+m,cmp);
    int l=1,r=0;
    rep(i,1,m){
        while(e[i].r>r) add(++r);
        while(e[i].r<r) del(r--);
        while(e[i].l-1<l) add(--l);
        while(e[i].l-1>l) del(l++);

        ans[e[i].id]=tmp;
    }
    rep(i,1,m){
        printf("%lld\n",ans[i]);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章