Xorto
题目描述
给定一个长度为 n n n 的整数数组,问有多少对互不重叠的非空区间,使得两个区间内的数的异或和为 0。
输入描述
第一行一个数 n n n 表示数组长度;
第二行 n n n 个整数表示数组; 1 ≤ n ≤ 1000 1\leq n\leq 1000 1≤n≤1000, 0 ≤ 0\leq 0≤ 数组元素 < 100000 \lt100000 <100000 。
输出描述
一行一个整数表示答案。
示例1
输入
3
0 0 0
输出
5
说明
( [ 1 , 1 ] , [ 2 , 2 ] ) , ( [ 1 , 1 ] , [ 3 , 3 ] ) , ( [ 1 , 1 ] , [ 2 , 3 ] ) , ( [ 1 , 2 ] , [ 3 , 3 ] ) , ( [ 2 , 2 ] , [ 3 , 3 ] ) ([1,1],[2,2]),([1,1],[3,3]),([1,1],[2,3]),([1,2],[3,3]),([2,2],[3,3]) ([1,1],[2,2]),([1,1],[3,3]),([1,1],[2,3]),([1,2],[3,3]),([2,2],[3,3])
思路:
假设两个互不重叠的非空区间为
[
a
,
b
]
,
[
x
,
y
]
[a,b],[x,y]
[a,b],[x,y] ,
b
<
x
b<x
b<x ,我们枚举
x
x
x 从 2 到
n
n
n ,然后每次只需将新增的端点
b
=
x
−
1
b=x-1
b=x−1 的区间,即
[
1
,
x
−
1
]
,
[
2
,
x
−
1
]
,
.
.
.
,
[
b
,
x
−
1
]
[1,x-1],[2,x-1],...,[b,x-1]
[1,x−1],[2,x−1],...,[b,x−1] 记录下来,这样就不用每次都从头算一遍,然后枚举当前
x
x
x 作为端点的区间
[
x
,
x
]
,
[
x
,
x
+
1
]
,
.
.
.
,
[
x
,
n
−
1
]
,
[
x
,
n
]
[x,x],[x,x+1],...,[x,n-1],[x,n]
[x,x],[x,x+1],...,[x,n−1],[x,n] ,看看前面所有记录下的
[
a
,
b
]
[a,b]
[a,b] 中有多少与之相等的(为什么要相等呢?因为异或的性质,x^x=0; a^b=c, c^b=a, 也就是一个数异或两次相当于没有异或)。
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int sum[1005];
int cnt[1 << 17];
int main()
{
ios::sync_with_stdio(false);
int n, a;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a;
sum[i] = sum[i - 1] ^ a;
}
ll ans = 0;
// [a,b], [x,y] 两个互不重叠的非空区间
for (int i = 2; i <= n; i++) //枚举 x 从 2 到 n ,从 2 开始是因为要给前一个区间留出位置
{
for (int j = 1; j < i; j++) //计算以 b = x-1 时 [a,b] 的增加的可能
cnt[sum[j - 1] ^ sum[i - 1]]++;
for (int j = i; j <= n; j++) //枚举 y 计算有几个 [x,y] 的值等于前面的 [a,b]
ans += cnt[sum[i - 1] ^ sum[j]];
}
cout << ans << endl;
return 0;
}