做法:知道异或也像加法一样满足前缀和。
即a[l]^a[l+1]^a[l+2]^a[l+3]……^a[r] = a[r]^a[l-1]
我们设a[l]^a[l+1]^a[l+2]^a[l+3]……^a[r] = k,那么k = a[r]^a[l-1]
异或还满足以下性质:
k = a[r]^a[l-1]
a[r] = k^a[l-1]
a[l-1] = k^a[r]
由于这道题是离线查询,所以我们可以使用莫队算法。
不过要注意,由于前缀和更新的是l-1和r ,所以我们一开始输入的时候要处理
AC代码:
#include<bits/stdc++.h>
#define rep(i,s,t) for(int i = (int)(s); i <= (int)(t); i++)
#define rev(i,t,s) for(int i = (int)(t); i >= (int)(s); i--)
#define pb push_back
#define sz(x) (int)(x).size()
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
const int maxm = 1e6+5;
struct query{
int l,r,id;
};
query q[maxn];
int block,curR,curL;
int n,m,k;
int sum[maxn];//异或前缀和
int cnt[maxm];//cnt[i]用来保存异或前缀和为i的数量
int ans[maxn];//用来存储每次查询的答案
int now;//用来保存计算的答案
bool cmp(query a,query b)
{
return a.l/block == b.l/block?a.r<b.r:a.l<b.l;
}
void add(int i)
{
cnt[i]++;
now+=cnt[i^k];
}
void del(int i)
{
now-=cnt[i^k];
cnt[i]--;
}
int main()
{
#ifdef LOCAL_FILE
freopen("in.txt","r",stdin);
#endif // LOCAL_FILE
// ios_base::sync_with_stdio(0);
// cin.tie(0),cout.tie(0);
scanf("%d %d %d",&n,&m,&k);
block = (int)sqrt(n);//块的大小
for(int i=1;i<=n;i++)
{
int d;
scanf("%d",&d);
sum[i] = sum[i-1]^d;//异或前缀和
}
for(int i=1;i<=m;i++)
{
scanf("%d %d",&q[i].l,&q[i].r);
q[i].l--;//因为计算答案用到了前缀和的缘故(区间[l,r],k = s[r]^s[l-1],所以这里使l-1,求得的才是正确的答案)
q[i].id = i;
}
sort(q+1,q+1+n,cmp);
curL = 1;
curR = 0;
for(int i=1;i<=m;i++)
{
while(curR<q[i].r) add(sum[++curR]);
while(curR>q[i].r) del(sum[curR--]);
while(curL<q[i].l) del(sum[curL++]);
while(curL>q[i].l) add(sum[--curL]);
ans[q[i].id] = now;
}
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
return 0;
}