题目链接
“题目讲解”
题目描述:
已知一个长度为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,还有异或符号的可逆。
其实我也很好奇为什么要用异或前缀和来维护。
后来经过多位学长共同合作才把我说服。
核心是: 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
*/