题解
2026-02-09 21:22:03
发布于:湖南
我们可以维护异或前缀和,那么 到 的异或和就为 ,问题就变成了询问区间 中,有多少对 和 满足
我们对于每个值,用莫队维护它出现的次数和在它之前满足与这个值异或起来为 的数的个数,添加或删除点时答案对应加上或减去维护的个数即可。
Code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+10,M=2e5+10;
int n,m,c;
int a[N];
struct Query { int l,r,id; } q[N];
int block;
int bel[N];
ll sum;
int tot[M];
ll ans[N];
bool cmp(Query a,Query b){
return (bel[a.l]^bel[b.l]) ? bel[a.l]<bel[b.l] : ( (bel[a.l]&1) ? a.r<b.r : a.r>b.r ) ;
}
void Build(){
block=pow(n,2.0/3.0);
for(int i=1;i<=n;i++) bel[i]=(i-1)/block+1;
}
void Add(int x) { sum+=tot[a[x]^c]; tot[a[x]]++; }
void Del(int x) { tot[a[x]]--; sum-=tot[a[x]^c]; }
int main(){
scanf("%d%d%d",&n,&m,&c);
Build();
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) a[i]^=a[i-1];
for(int i=1;i<=m;i++){
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id=i;
}
sort(q+1,q+1+m,cmp);
tot[0]=1;
int l=0,r=0;
for(int i=1;i<=m;i++){
int ql=q[i].l-1,qr=q[i].r,id=q[i].id;
while(l<ql) Del(l++);
while(l>ql) Add(--l);
while(r<qr) Add(++r);
while(r>qr) Del(r--);
ans[id]=sum;
}
for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
return 0;
}
1
说几个坑点和不好理解的点吧 :
: 由于询问是对于区间 的,所以询问范围为 ,又因为 ,所以要写上这句话,不然对于异或前缀和为 的位置,显然有一个合法区间 ,其所对应的 为 ,但由于 ,没有统计答案。
: 由于询问范围为 ,自然 初始值为 (这个 因为这个调了半天)。
: 由于询问是对于区间 。
: 由于询问是对于区间 的,且 ,所以在异或前缀和数组中这一定是两个位置,如果先写第二句话的话,当 时, 在统计答案时会会把当前位置算进当前位置的答案中加上,显然不合法,于是多算答案。
: 其实大致思路同上一条,由于询问是对于区间 的,且 ,所以在异或前缀和数组中这一定是两个位置,如果先写第二句话的话,当 时, 在统计答案时会把当前位置算进当前位置的答案中减去,显然不合法,于是少算答案。
由于异或前缀和可能大于 ,最大值为 ,故开这么大。
l 和 r 的加减与 和 的先后循序 : 删除是删除当前位置,故先 再加减,添加是添加下一个位置,故先加减再 Add (这应该没人错吧 qwq )。
由于是区间数量,记得开 long long。
对于上文第五条错误 hack :
2
4 1 0
0 1 0 1
2 4
正确输出 :
2
错误输出 :
1
对于上文第八条错误 hack : (叉了6篇)
2
100000 1 0
(100000个0)
1 100000
正确输出 :
5000050000
错误输出 :
705082704
这里空空如也




有帮助,赞一个