UPC 6759/BZOJ 5301:异或序列(莫队算法+异或)

 题目链接

“题目讲解”

题目描述:

已知一个长度为n的整数数列a1,a2,…,an,给定查询参数l、r,问在al,al+1,…,ar区间内,有多少子序列满足异或和等于k。也就是说,对于所有的x,y(l≤x≤y≤r),满足ax⊕ax+1⊕⋯⊕ay=k的x,y有多少组。


输入:

输入第一行为3个整数n,m,k。第二行为空格分开的n个整数,即a1,a2,…,an。接下来m行,每行两个整数lj,rj,代表一次查询。


输出:

输出共m行,对应每个查询的计算结果。


样例输入:

4 5 1
1 2 3 1
1 4
1 3
2 3
2 4
4 4

样例输出:

4
2
1
2
1

提示:

对于30%的数据,1≤n,m≤1000。
对于100%的数据,1≤n,m≤105,0≤k,ai≤105,1≤lj≤rj≤n。


题解:

莫队算法的第一题,正儿八经的一道莫队题目;我真的挺开心,当我学会了莫队怎么用两个指针挪动的时候。

我先讲解一下 , 莫队的必须要遵循的东西。

  • 1、复杂度 O(  n ^ 1.5 ) 只能处理1e5一下的数据量
  • 2、必须离线操作
  • 3、莫队算法核心是,加入一段连续序列时,必须要有  add 和 del 的维护
  •       而且这个维护一定是需要有一对互逆运算的,比如 + 对应 - ,× 对应 /,^ 对应 ^

在我看来这个题目更多的是考查 异或运算符的性质特点。

而不是考查大家看不看得出来是莫队算法,其实容易判断,

离线操作,询问区域,n<=1e5,还有异或符号的可逆。

 其实我也很好奇为什么要用异或前缀和来维护。

后来经过多位学长共同合作才把我说服。

扫描二维码关注公众号,回复: 2682714 查看本文章

核心是:   pre [  i  ] ^ K = pre [  j  ]    首先介绍一下,Pre [ i ] 表示 第 i 位的前缀异或和。

然后我们只要维护好这个前缀和  出现的值的次数即可。

为什么呢???我在很多博客都没看到为什么,只有一个博客大概说了说异或区间的问题。

我们首先知道:

前缀异或和 : pre[ i ] = a[1]^a[2]a[3]^a[4]^…………^a[n].

然后某一段的  区域的异或和  :[ i, j ]  =  a[i]^a[i+1]^...^a[j]=pre[j]^pre[i-1]

pre[j]^pre[i-1]=k

pre[i-1]^k=pre[j] 

pre[j]^k=pre[i-1]

只要我们记录每一个前缀异或和出现的次数:当我们访问 i 时,        pre[ i ] ^ k = pre[ j ]  ,cnt[ pre[ j ] ] ++;

cnt [ 对应的前缀异或和 ] = 出现的次数。

可能你还比较难理解,我就画一个图吧。

/*
    author: Osea
    if you ask me how much i love you,
    the moonlight stand for my heart.
*/
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+1e5;
typedef long long ll;
void read(ll &ans){
    ll x=0,f=1;char c=getchar();
    for( ; !isdigit(c) ;c=getchar() ) { if(c=='-') f=-1;}
    for( ;  isdigit(c) ;c=getchar() ) { x=x*10+c-'0';}
    ans=x*f;
}
ll pre[N],ans[N],n,m,k,unit,tmp,tot;
typedef struct node{
    ll L,R,No;
    bool operator <(const node &p)const {
        return L/unit!=p.L/unit ? L/unit < p.L/unit : R<p.R;
    }
}node;
node Q[N];
int cnt[N];
void add(int x){
    tmp+=cnt[x^k];
    cnt[x]++;
}
void del(int x){
    cnt[x]--;
    tmp-=cnt[x^k];
}
void solve(){
    unit=(int)sqrt(n*1.0);
    sort(Q+1,Q+m+1);
    int L=0,R=0;
    tmp=0;
    cnt[0]=1;
    for(int i=1;i<=m;i++){
        while( R < Q[i].R ) { add( pre[++R] ); }
        while( R > Q[i].R ) { del( pre[R--] ); }
        while( L < Q[i].L ) { del( pre[L++] ); }
        while( L > Q[i].L ) { add( pre[--L] ); }
        ans[Q[i].No]=tmp;
        /*printf(" L :%d  R :%d\n",Q[i].L,Q[i].R);
        for(int i=0;i<=n;i++){
            printf("cnt[ %d ] = %d\n",i,cnt[i]);
        }
        printf("\n");*/
    }
    for(int i=1;i<=m;i++){
        printf("%lld\n",ans[i]);
    }
}
int main(){
    read(n),read(m),read(k);
    for(int i=1;i<=n;i++)   {read(pre[i]),pre[i]^=pre[i-1];}
    for(int i=1;i<=m;i++)   {read(Q[i].L),read(Q[i].R),Q[i].No=i;Q[i].L--;}
    //for(int i=1;i<=n;i++) printf("%d%c",pre[i],i==n?'\n':' ');
    solve();
    return 0;
}
/*
4   5   1
1   2   3   1
1   4
1   3
2   3
2   4
4   4
*/

猜你喜欢

转载自blog.csdn.net/Z_sea/article/details/81537556