异或和之和
P9236 \[蓝桥杯 2023 省 A\] 异或和之和 - 洛谷 | 计算机科学教育新生态
问题
给定一个数组 $A_i$,分别求其每个子段的异或和,并求出它们的和。或者说,对于每组满足 $1 \leq L \leq R \leq n$ 的 $L,R$,求出数组中第 $L$ 至第 $R$ 个元素的异或和。然后输出每组 $L,R$ 得到的结果加起来的值。
输入格式
输入的第一行包含一个整数 $n$ 。
第二行包含 $n$ 个整数 $A_i$,相邻整数之间使用一个空格分隔。
输出格式
输出一行包含一个整数表示答案。
样例 #1
样例输入 #1
1 2
5 1 2 3 4 5
样例输出 #1
1
39
提示
【评测用例规模与约定】
对于 $30 %$ 的评测用例,$n \leq 300$;
对于 $60 %$ 的评测用例,$n \leq 5000$;
对于所有评测用例,$1 \leq n \leq 10^5$,$0 \leq A_i \leq 2^{20}$。
我的解答(未AC)
|
|
通过异或前缀和数组暴力枚举算出ans,时间复杂度为O(n²),只能拿到60分
优化
要对前缀异或数组中元素两两异或的时间复杂度$O(n^2)$进行优化,考虑到异或的性质,我们可以选择将整数转为二进制,按位运算:
|
|
考虑到数字的范围$2^{20}$,我们开a[20]来存每一个元素的每一位;
a[i][j] = (x >> j) & 1;
的作用是求出x
二进制的每一位,方法是先右移再和1做位与运算a[i][j] ^= a[i - 1][j];
即在每一位上求异或前缀和
现在求得了所有元素的异或前缀和数组,这样我们可以在每一位上两两异或并累加求得该位的区间异或和 这里我们使用空间换时间的方式,将$n^2$压缩到$n$:
|
|
这里对代码进行逐行解释:
|
|
a[i][j] ^ 1
是对该位取反,属于异或运算的基本应用- 如果异或和区间在该位上的元素(0、1)想对最终结果做出贡献,就必须让$a_i\oplus a_{i+1}\oplus…\oplus a_m$的结果为$1$,在异或前缀和上就是$b_{i-1}\oplus b_{m} $的结果为$1$:由于这里只有$0、1$两个元素,某个元素的异或就是另一个元素,我们使用map(vector[2]同理)来存储0和1的出现次数,以空间换时间;
- x即与
a[i][j]
异或值为1的元素个数
|
|
-
(1 << j) * x;
对1左移$j$位再乘上x,即异或前缀和元素在该位上两两异或为1的总次数,并将其还原成十进制计算结果 -
m[a[i][j]]++
记录该位,用于后续该位元素的计算
通过这种方法,我们将时间复杂度$O(n^2)$压缩到了$O(20n)$,成功AC
总结
这道题的思路在于,题目给出了$A_i$元素的范围$2^{20}$,提示了我们可以将他转换成二进制数,也就是拆位;
又因为每一位只可能是1或0,我们可以借助长度为2的数组对记录元素出现的次数,避免了双重循环