617.E . XOR and Favorite Number 莫队+异或前缀和

题目链接: https://codeforces.com/contest/617/problem/E

题意:

长为 1 e 5 1e5 1e5 的数组 a a a ,和 1 e 5 1e5 1e5 个查询,每次查询要求出区间 [ l i , r i ] [l_i,r_i] [li,ri] 中有多少 个子区间使得该区间内的异或和为 k k k

做法:

因为我们要快速求得一个区间 [ l i , r i ] [l_i,r_i] [li,ri] 中的异或和,所以我们大可以先把所有的值 a [ i ] a[i] a[i] 变成 a [ 1 ] a[1] a[1] ^ a [ 2 ] . . . a [ i ] a[2]...a[i] a[2]...a[i] 这样我们就可以在 O ( 1 ) O(1) O(1) 的时间里得到区间的和,即 [ l i , r i ] = a r [l_i,r_i]=a_r [li,ri]=ar ^ a l − 1 a_{l-1} al1

然后每次对边界进行加减就变得很快,莫队的思想就出现了。

比如我们要将右区间变大,那么对于原来的区间 [ l , r − 1 ] [l,r-1] [l,r1] 中的影响会先保存在数组 n u m num num 里,若加入 r r r 之后存在 x x x 个区间 x o r [ l i , r ] = k xor[l_i,r]=k xor[li,r]=k ,那么一定有 n u m [ a r num[a_r num[ar ^ k ] = x k]=x k]=x ,因为 a l a_l al 是前缀异或和, a l a_l al ^ a r a_r ar 其实就是区间 [ l + 1 , 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;
}
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页