链接:https://www.lydsy.com/JudgeOnline/problem.php?id=4017
(这是一道好题)给你n个数,分别问这n个数中任意两个区间的异或和的加和是多少、任意两个区间的和的异或和是多少。
Task 1:求异或的和
针对第一问,我们首先分析出两个性质:
- 设[1,i]的异或和为xor[i],那么区间[l,r]内数字的异或和为xor[l−1]⨁xor[r]。
- 某个在数字第k位有贡献的子区间[l,r],它们的前缀异或和xor[l]与xor[r]的值一定不同。而且很显然,这个区间的贡献为2k。
有上述的两个性质,我们的目标其实就是拆分数位,针对每个数位,找到整个[1,n]区间内有多少对前缀异或和结果不同。假设在[1,n]中的第k位数字有x个数该位是1,那么就有n−x个位置是0。这样只是找到了区间长度≥2的贡献,但是题目规定单个数字也算贡献,不妨再加上一个x,问题转化成在两个数字集合中分别挑选一个数字,问有多少对数字,因此第k位总体贡献是x(n−x+1)2k。
实现就很简单,我们针对每一位k,求异或和的每一步时,更新这个x,最后算一下贡献就行了。
1 | LL gao1() { |
Task 2:求和的异或
考虑和第一问一样的做法,要求的答案为异或和,考虑这个和的第k位什么时候为1:当出现的在这一位为1时的区间[l,r]内数字之和为奇数个的时候贡献为2k。
我们又能发现一个规律:当区间[l,r]的和的第k位是奇数时,有:
(s[r]−s[l−1]) mod 2k+1 ≥ 2k
针对每一位k,我们可以预处理出来前i项的和s[i],离散化这个s以后用树状数组维护每个位置i左右两侧是否满足上述条件。
1 | vector<LL> bit; |
通过这个题,学到了要去讨论所求结果和计算过程的特点这样的思路。例如本题的两个问就是要针对区间内的异或和或答案的异或和中的每一位做讨论。
于是,遇到异或和考虑到先讨论一下每一位的贡献就对啦~
提交的时候报了CE,emmm懒得改就这样吧
1 |
|
v1.5.2