题目链接:
http://codeforces.com/contest/617/problem/E
题目大意:
有n 个数,m个询问。每次询问在区间[l,r]里面,有多少种情况使得ai^ai+1^……^aj=k。
范围:1 ≤ n, m ≤ 100 000, 0 ≤ k ≤ 1 000 000。
思路:
对于这类区间上的问题,我们可以获得数组的异或前缀和pre[i]=a1^a2......ai。
这样对于区间[l,r],我们可以知道这个区间的异或值为pre[l-1]^pre[r]。
对于不需要修改的区间询问问题,可以采用莫队算法。
莫队算法:在我现在粗浅的认知看来,莫队算法的主要精髓就是面对已知的询问信息,将这些询问进行顺序调整然后离线操作。
例如对于多个询问[l1,r1],[l2,r2]……[li,ri],如果已知了[l1,r1]的结果,那么我们肯定很快能够求出[l1+1,r1+1],[l1-1,r1+1],[l1-1,r1-1],[l1+1,r1-1]的结果。所以当我们去看第二个询问的时候,我们就可以通过第一个询问的结论,一步步地从[l1,r1]走向[l2,r2],此时我们所需要的时间就是O(|L2-L1|+|R2-R1|),这个有一个名字叫做曼哈顿距离。于是问题就变成了构造曼哈顿最小生成树,然后按照生成树的顺序进行求解,使得速度大大提升。
但是求曼哈顿生成树比较困难,然而还有一种分块思想,能够比较迅速而高效地解决这个问题。
我们把n个数分块成为sqrt(n)块,然后把询问进行排序:如果两个询问的l是在同一块里面的,就按r从小到大排。如果不同块,就按照块从小到大排。在同一个块里面转移的时候,由于每个块里面有n^0.5个元素,所以转移最大时间复杂度就是O((n^0.5)*(n^0.5))=O(n)。如果在不同块,最大复杂度是O(n^1.5)。所以最终时间复杂度为O(n^1.5)。
------------------------------------------------------------------------------
所以对于这个问题,我们已经知道了前缀和,所以可以知道:如果pre[i-1]^pre[j]=k,那么就有pre[j]^k=pre[i-1]。这个时候我们开一个数组num,来记录pre[i-1]^pre[j]=k时候的个数,我们转移时就只需要对答案sum+=num[pre[i-1]^pre[j]]就可以了,再化简一下就是,sum+=num[pre[j]^k]。
代码:
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<stdlib.h>
#define ll __int64
#define M 100010
using namespace std;
ll n,m,k,a[M],pre[M],ans[M],block,pos[M],num[M*50],sum;
struct node {
ll l,r,id;
}Q[M];
bool cmp(node a,node b )
{
if(pos[a.l]==pos[b.l])return a.r<b.r;
return pos[a.l]<pos[b.l];
}
int main()
{
ll i,j;
while(~(scanf("%I64d%I64d%I64d",&n,&m,&k)))
{
sum=0;
memset(num,0,sizeof(num));
memset(pre,0,sizeof(pre));
block=ceil(sqrt(1.0*n)); //分块
for(i=1;i<=n;i++)
{
scanf("%I64d",&a[i]);
if(i==1)pre[i]=a[i];
else pre[i]=pre[i-1]^a[i]; //求前缀数组
pos[i]=(i-1)/block;
}
for(i=1;i<=m;i++)
{
scanf("%I64d%I64d",&Q[i].l,&Q[i].r);
Q[i].id=i;
// pos[i]=i/block;
}
sort(Q+1,Q+m+1,cmp);
ll L,R;
L=1;R=0;
num[0]++;
for(i=1;i<=m;i++)
{
ll id=Q[i].id;
while(R<Q[i].r){
R++;
sum+=num[pre[R]^k];
num[pre[R]]++; //记录出现次数
}
while(R>Q[i].r)
{
num[pre[R]]--;
sum-=num[pre[R]^k];
R--;
}
while(L<Q[i].l)
{
num[pre[L-1]]--;
sum-=num[pre[L-1]^k];
L++;
}
while(L>Q[i].l)
{
L--;
sum+=num[pre[L-1]^k];
num[pre[L-1]]++;
}
// printf("%I64d %I64d\n",L,R);
ans[id]=sum;
}
for(i=1;i<=m;i++)
printf("%I64d\n",ans[i]);
}
}