题目链接
题面
题目大意
给你一个长度为n的数组,数组内数据范围为(0 ≤ ai < 220),找出连续子序列(al ~ ar)满足以下要求:
①r-l+1为偶数
②取mid=(l+r-1)/2,使 al 至 amid 区间内的异或值与 amid+1 至 ar 区间内的异或值相同。
求满足上述要求的连续子序列共有多少。
题目分析
此处采用前缀异或的思想,设 Si 为 a1 至 ai 区间内的异或值。
由 a ⊕ b ⊕ b = a ⊕ 0 = a,可以推出 :
al ⊕ al+1 ⊕ … ⊕ amid = Smid ⊕ Sl-1
amid+1 ⊕ amid+2 ⊕ … ⊕ ar = Sr ⊕ Smid
由 a ⊕ c = a ⊕ d ==> c=d,可以推出 Sr = Sl-1。
因此只需计算前缀异或相同的位置可组成多少满足要求的连续子序列。
又因r-l+1为偶数,则 r 与 l-1 同为奇数或偶数。
即只要找出前缀异或相同的情况下,并且分别计算下标为奇数和偶数的数量,分别对奇数与偶数求两两组合即可。
代码
读入数据时,计算当前位的前缀异或值,并使用结构体保存该值与当前下标。题目范围较大,暴力匹配会超时,因此将结构体数组按前缀异或排序,这样使前缀异或值相同的数据相邻排列。在前缀异或值相同的区间内分别记录下标的奇偶数量,计算答案。扫描一遍数组即可得到答案,时间复杂度为O(n)。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
struct node{ll t;int ind;}xo[300010];
bool cmp(node a,node b){
if(a.t==b.t) return a.ind<b.ind;
return a.t<b.t;
}
int main()
{
int n,k,a,b;
scanf("%d",&n);
for(int i=1;i<=n;i++){
ll tmp;
scanf("%I64d",&tmp);
xo[i].t=xo[i-1].t^tmp;
xo[i].ind=i;
}
sort(xo+1,xo+n+1,cmp);
ll ans=0;
for(int i=0;i<=n;i++){
ll ji=0,ou=0;
for(int j=i;j<=n;j++){
if(xo[i].t!=xo[j].t){
//cout<<i<<" "<<j<<" "<<ji<<" "<<ou<<endl;
ans+=(ji*(ji-1)/2)+(ou*(ou-1)/2);
i=j-1;
break;
}
if(xo[j].ind%2) ji++;
else ou++;
if(j==n){
ans+=(ji*(ji-1)/2)+(ou*(ou-1)/2);
i=n;
break;
}
}
}
printf("%I64d\n",ans);
return 0;
}