题意:
n个数,m个区间,问区间内有多少对(i,j)a[i]^a[i+1]^......a[j-1]^a[j]==k,k已给出。
解题思路:
离线的查询,用莫队最好了。
第一次写莫队,所以理解还比较浅。
莫队算法的原理是基于由已知[l,r]的答案推到[l,r-1],[l-1, r],[l, r+1], [l+1, r]的答案是o(1)的,所以我们可以先算出一个区间的答案,然后再逐步由此去求出其它区间的答案,步数是两个区间的曼哈顿距离(把区间看成点的话),这样做的话极限数据是会卡tle的。
然后莫队优化的地方就是在于把询问区间按左端点分块了,然后排序,让左端点坐在的块的编号小的在前面,相等的时候让右端点小的在前面。
这样的话,由i区间移动到i+1区间,左端点的移动就肯定不会超过sqrt(n)了
右端点的话,在一个块内是单调递增的,在一个递增区间内r的差距最大为n,且只会出现一次,因为 单调,然后总共有n^0.5块,所以右端点的总更新时间也不会超过o(nsqrt(n)).
跨越一个块的话,差距也最多是n,且每两个相邻的块只会出现一次,总共有n^0.5块,所以更新的时间复杂度是o(nsqrt(n));
然后这个题具体来讲需要讲一下的时候add函数和del函数了,在代码里讲吧。
代码:
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn=1<<20;
struct p
{
int l, r, id;
}block[maxn];
int pos[maxn];
LL book[maxn];
int n, m, k;
LL ANS=0;
LL ans[maxn];
int a[maxn];
bool cmp(p a, p b)
{
if(pos[a.l]==pos[b.l])
{
return a.r<b.r;
}
else return pos[a.l]<pos[b.l];
}
int L=1, R=0;
void del(int x)
{
book[a[x]]--; //book是统计1,i区间值为y的区间有多少个,这里x点删去,就删去对应的值
ANS-=book[a[x]^k]; //[i,j]异或值为a[j]^a[i],book[a[x]^k]就是询问有多少个a[i]的值为a[x]^k,对应的就是[i,x]值为k的有多少对,也就是x对应的答案了,这里需要减去x对应的答案
}
void add(int x)
{
ANS+=book[a[x]^k];
book[a[x]]++;
}
int main()
{
int i, j;
cin>>n>>m>>k;
int sz=sqrt(n);
for(i=1; i<=n; i++)
{
scanf("%d", &a[i]);
a[i]=a[i]^a[i-1];
pos[i]=i/sz;
}
for(i=1; i<=m; i++)
{
scanf("%d%d", &block[i].l, &block[i].r);
block[i].id=i;
}
sort(block+1, block+m+1, cmp);
book[0]=1;
for(i=1; i<=m; i++)
{
while(L<block[i].l)
{
del(L-1);
L++;
}
while(L>block[i].l)
{
L--;
add(L-1);
}
while(R>block[i].r)
{
del(R);
R--;
}
while(R<block[i].r)
{
R++;
add(R);
}
ans[block[i].id]=ANS;
}
for(i=1; i<=m; i++)printf("%lld\n", ans[i]);
}