problem
给定一个长度为n的整数数组,问有多少对互不重叠的非空区间,使得两个区间内的数的异或和为0。
Input
第一行一个数n表示数组长度;
第二行n个整数表示数组;
1<=n<=1000,0<=数组元素<100000。
Output
一行一个整数表示答案。
Sample Input
3
0 0 0
Sample Output
5
Hint
([1,1],[2,2]),([1,1],[3,3]),([1,1],[2,3]),([1,2],[3,3]),([2,2],[3,3])
思路
首先异或运算有一些性质
名称 | 示例 |
---|---|
交换律 | a^b=b^a |
结合律 | (a^b)^c = a^(b^c) |
对于任何数x | x^x=0,x^0=x |
自反性 | A XOR B XOR B = A xor 0 = A (重要) |
对于本题
由于数据量为1000,n^2为1e6,如果能找到方法,时间复杂度是允许的。
关键在于这个不重叠的非空区间怎么考虑
可以考虑n^2枚举区间,算出其xor,然后**找前面(避免区间重复)**xor相同的另一个区间。但如果纯暴力,对每一个枚举的区间,需要在前面再花O(n^2)来找,爆炸。
现在有两个地方可以优化
1.维护一个数组sum [i],记录arr[i]前ixor值,由上面自反性质知:x=sum[j] ^ sum[i-1];
2.对于找前面区间,采用权值数组,这是因为数据为1e5,而无论怎么异或,不会超过2e5的(胡算),所以可行。具体就是,对于每一次枚举区间后,顺便记录下前i个xor值,并权值下,这样后面需要找前面区间xor相同值时,只需访问quan[xor],即可直接获得数量。也就是说,由于本题区间不重叠,加上数比较小,因此可以这样在枚举时,记录前面的信息(也只需要前面的)。这样复杂度为2n^2。
代码示例
#include<bits/stdc++.h>
using namespace std;
int arr[1010];
int sum[1010];
int quan[200010];
int main()
{
ios::sync_with_stdio(false);
int n;
cin>>n;
for(int i=1;i<=n;++i){
cin>>arr[i];
}
sum[0]=0;
for(int i=1;i<=n;++i){
sum[i]=sum[i-1] ^ arr[i];
}
long long ans=0;
for(int i=1;i<=n;++i){
for(int j=i;j<=n;++j){
int x=sum[j] ^ sum[i-1];
ans+=quan[x];
}
for(int j=1;j<=i;++j) quan[sum[i] ^ sum[j-1]]++;
}
cout<<ans<<endl;
return 0;
}