题面
对于给定的 n
,计算:
(nmod1)⊕(nmod2)⊕⋯⊕[nmod(n−1)]
其中 ⊕
表示按位异或。
数据范围: n≤1012
题面链接:CCPC2018 Hangzhou Problemset(L 题)
提交链接:HDUOJ 6275: Mod, Xor and Everything
由于 HDUOJ 卡常丧心病狂,建议读者前往
LibreOJ 6344: 异或和
提交(注意输入格式略微存在出入)。
分析
对于异或运算,我们不妨依据位运算的位独立性对于其二进制下的每一位逐位考虑。
对于数 n
,其二进制第 k
位上的值为:
⌊2kn⌋(mod2)
那么,对于第 k
位,经过题目中异或运算后这一位上的答案为:
i=1∑n⌊2knmodi⌋(mod2)
我们对其做一点小小的变换:
=i=1∑n⌊2knmodi⌋(mod2)i=1∑n⌊2kn−⌊in⌋i⌋(mod2)
我们发现这个式子非常类似于类欧几里德可解决的 f
式子(有一点不一样,但是后文会通过一些变换使其一样)。有关类欧几里德算法的相关内容可参考我的另一篇博客:浅谈类欧几里德算法。
至于 ⌊in⌋
,我们可以对其进行整除分块。
对于满足 ⌊in⌋=t
的某一块,我们记块的最左端为 l
,最右端为 r
,显然有:
⌊ln⌋=⌊rn⌋=t
以及:
nmodr=n−⌊rn⌋r=n−tr
那么对于这一块内求和:
i=l∑r⌊2k−ti+n⌋=====i=−r∑−l⌊2kti+n⌋i=0∑r−l⌊2kt(i−r)+n⌋i=0∑r−l⌊2kti+(n−tr)⌋i=0∑r−l⌊2kti+(nmodr)⌋f(t,nmodr,2k,r−l)
简而言之,我们首先通过整除分块得到每一块的 l
和
r
,并求出这一段中每一位对应的结果,最后将所有段的结果求和并将不同位“组装”起来就是最终答案了。
复杂度:O(nlogn)
。
这道题卡常呜呜呜。
在 n
比较小的时候,O(n)
暴力是比类欧
O(nlogn)
跑得还要快的…… 故考虑设定一个阈值 lim
,当 i
不大于 lim
的时候跑暴力,大于 lim
的时候跑类欧。对于本弱写的代码,实测得到
lim
大致取值在 2×107∼4×107
范围内时可以在 HDUOJ 上卡进时限。而在 LibreOJ 上这个范围就要宽很多了。
最后附上
我的代码
以供参考。
%%%