子数组异或和
子数组异或和
给定一个长度为 $n$ 的整数数组 $a_1,a_2, \dots ,a_n$。
请你统计一共有多少个数组 $a$ 的非空连续子数组能够同时满足以下所有条件:
- 该连续子数组的长度为偶数。
- 该连续子数组的前一半元素的异或和等于其后一半元素的异或和。
例如,当给定数组为 $[1,2,3,4,5]$ 时,满足条件的连续子数组只有 $1$ 个:$[2,3,4,5]$。
输入格式
第一行包含整数 $n$。
第二行包含 $n$ 个整数 $a_1,a_2, \dots ,a_n$。
输出格式
一个整数,表示满足条件的连续子数组的数量。
数据范围
前三个测试点满足 $2 \leq n \leq 10$。
所有测试点满足 $2 \leq n \leq 3 \times {10}^{5},0 \leq a_i < 2^{20}$。
输入样例1:
5 1 2 3 4 5
输出样例1:
1
输入样例2:
6 3 2 2 3 7 6
输出样例2:
3
输入样例3:
3 42 4 2
输出样例3:
0
解题思路
这题昨天想了很久都没想出来怎么做,想到了用异或前缀和,但没发现题目第二个隐含的性质,现在做题的抽象能力十分差,很难把一个问题抽象成学过的模型。
假设有异或和$s_i = s_1 \wedge s_2 \wedge \dots \wedge s_i$,根据同一个数的异或和为$0$这个性质,那么有$$\begin{align*} s_{i - j} &= s_1 \wedge s_2 \wedge \dots \wedge s_{i - j} \\ &= (s_1 \wedge \dots \wedge s_i) \wedge (s_1 \wedge \dots \wedge s_{j-1}) \\ &= s_i \wedge s_{j - 1} \end{align*}$$
对于第二个条件,假设子数组前一半的异或和为$x$,后一半的异或和为$y$,那么就有$x \wedge y = 0$,因此有$x = y$。
因此可以枚举子区间的右端点$i$,找到左端点$j+1$,满足区间长度$i - (j + 1) + 1 = i - j$为偶数,同时满足$s_i \wedge s_{j} = 0$,即$s_i = s_{j}$。因此我们要在$0 \sim i - 1$中找到有多少个$j$满足$s_i \wedge s_{j} = 0$,同时$i-j$为为偶数,即满足$i$和$j$的奇偶性相同。因此可以开两个哈希表,分别统计$j$为奇偶时的$s_j$。
AC代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int N = 1 << 20; 5 6 int cnt[2][N]; // 因为ai<2^20,因此异或和不超过2^20 7 8 int main() { 9 int n; 10 scanf("%d", &n); 11 12 long long ret = 0; 13 cnt[0][0] = 1; 14 for (int i = 1, sum = 0; i <= n; i++) { 15 int v; 16 scanf("%d", &v); 17 sum ^= v; 18 ret += cnt[i & 1][sum]; 19 cnt[i & 1][sum]++; 20 } 21 22 printf("%lld", ret); 23 24 return 0; 25 }
参考资料
AcWing 4507. 子数组异或和(AcWing杯 - 周赛):https://www.acwing.com/video/4213/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/16586364.html