题目链接:HDOJ6304
题意:根据数列递推给了你这么一个式子,求前n项和mod 1e9+7
题目分析
递推很复杂,下标含有项,一般来说是求不出通项的,所以$O(1)$的算法应该是没有的,数据范围又很大 n最大1e18 时限1000 分析一下 $O(n)$、$O(\sqrt n)$啥的大概是不行的 对数级别的可以考虑一下
初级打表
一开始拿到题目毫无头绪 打表看一下$a_n$前几项有没有规律
前100项:
a_1 = 1
a_2 = 1
a_3 = 2
a_4 = 2
a_5 = 3
a_6 = 4
a_7 = 4
a_8 = 4
a_9 = 5
a_10 = 6
a_11 = 6
a_12 = 7
a_13 = 8
a_14 = 8
a_15 = 8
a_16 = 8
a_17 = 9
a_18 = 10
a_19 = 10
a_20 = 11
a_21 = 12
a_22 = 12
a_23 = 12
a_24 = 13
a_25 = 14
a_26 = 14
a_27 = 15
a_28 = 16
a_29 = 16
a_30 = 16
a_31 = 16
a_32 = 16
a_33 = 17
a_34 = 18
a_35 = 18
a_36 = 19
a_37 = 20
a_38 = 20
a_39 = 20
a_40 = 21
a_41 = 22
a_42 = 22
a_43 = 23
a_44 = 24
a_45 = 24
a_46 = 24
a_47 = 24
a_48 = 25
a_49 = 26
a_50 = 26
a_51 = 27
a_52 = 28
a_53 = 28
a_54 = 28
a_55 = 29
a_56 = 30
a_57 = 30
a_58 = 31
a_59 = 32
a_60 = 32
a_61 = 32
a_62 = 32
a_63 = 32
a_64 = 32
a_65 = 33
a_66 = 34
a_67 = 34
a_68 = 35
a_69 = 36
a_70 = 36
a_71 = 36
a_72 = 37
a_73 = 38
a_74 = 38
a_75 = 39
a_76 = 40
a_77 = 40
a_78 = 40
a_79 = 40
a_80 = 41
a_81 = 42
a_82 = 42
a_83 = 43
a_84 = 44
a_85 = 44
a_86 = 44
a_87 = 45
a_88 = 46
a_89 = 46
a_90 = 47
a_91 = 48
a_92 = 48
a_93 = 48
a_94 = 48
a_95 = 48
a_96 = 49
a_97 = 50
a_98 = 50
a_99 = 51
a_100 = 52
好像没什么规律
高级打表
不过我们好像注意到总是连续的几个值一起出现
那我们统计一下每一个值出现的次数
表:
value: 1 times: 2
value: 2 times: 2
value: 3 times: 1
value: 4 times: 3
value: 5 times: 1
value: 6 times: 2
value: 7 times: 1
value: 8 times: 4
value: 9 times: 1
value: 10 times: 2
value: 11 times: 1
value: 12 times: 3
value: 13 times: 1
value: 14 times: 2
value: 15 times: 1
value: 16 times: 5
value: 17 times: 1
value: 18 times: 2
value: 19 times: 1
value: 20 times: 3
value: 21 times: 1
value: 22 times: 2
value: 23 times: 1
value: 24 times: 4
value: 25 times: 1
value: 26 times: 2
value: 27 times: 1
value: 28 times: 3
value: 29 times: 1
value: 30 times: 2
value: 31 times: 1
value: 32 times: 6
value: 33 times: 1
value: 34 times: 2
value: 35 times: 1
value: 36 times: 3
value: 37 times: 1
value: 38 times: 2
value: 39 times: 1
value: 40 times: 4
value: 41 times: 1
value: 42 times: 2
value: 43 times: 1
value: 44 times: 3
value: 45 times: 1
value: 46 times: 2
value: 47 times: 1
value: 48 times: 5
value: 49 times: 1
value: 50 times: 2
看一下这个times表 可以发现奇数出现的次数为1(不要管那个1 因为那是首项限制了)
出现2次的数是2 6 10 14 …
出现3次的数是 4 12 20 28 …
出现4次的数是 8 24 40 56 …
…
出现i次的数是$2^{i-1}$ (1 3 5 7 9 …)
观察一下这些数的二进制表达
可以发现 i 这个数一共出现了
__builtin_ctz(i)+1
次或者说 出现了$log_2{lowbit(i)}+1$次
没错 这个lowbit就是那个树状数组的lowbit
算法分析
我们先把$a_1$剔除掉 因为这个首项存在有点碍事 之后再加回来就行了
那么数列所有项往前面移一个下标 所询问的n也相应的要-1
便于叙述 我们记 $f(x)=log_2{lowbit(i)}+1$
根据之间打的$a_i$值表 我们要求的$a_n$之前应该有许多个一块一块值一样的项
$a_n$周围应该有一些(也有可能没有)和它值一样的项
由于我们是求$a_i$的前n项和 那我们把$a_n$之前项一块一块一起算(反正值一样,出现次数也可以计算)
那么首先我们有一个问题——$a_n$的值是多少
问题1:$a_n$的值
便于叙述 我们记$sum(x)=\sum_{i=1}^x{f(i)}$表示值为1~x一共有多少项
如果我们朴素的找 应该是从头开始往后扫n个 这样太慢 我们直接算
找一个最大的t使得:
$\sum_{k=1}^{t}f(k)\le n$
那么 $a_n$的值分两种情况
- $\sum_{k=1}^{t}f(k) = n$ 这个时候 $a_n=t$ ($a_n$正好位于值为$t$的那一堆数的最后一个)
- $\sum_{k=1}^{t}f(k) < n$ 这个时候 $a_n=t+1$ ($a_n$位于值为$t+1$那一堆数中)
然后我们要开始计算$a_n$之前数的和
问题2:前一部分和
便于叙述 我们记$psum(x)=\sum_{i=1}^x{i\cdot f(i)}$表示值为1~x的所有项和
由于有可能一个值相同的块被$a_n$切开(只有$a_n$之前的项对答案有贡献,后面的没有)
那么我们先计算$a_n$之前的完整块的和,所谓前一部分和
前一部分和为$psum(t)$
问题3:后一部分和
后一部分和就是与$a_n$处在同一块下对答案有贡献的项总和
当然如果正好是问题1的情况1
- $\sum_{k=1}^{t}f(k) = n$ 这个时候 $a_n=t$ ($a_n$正好位于值为$t$的那一堆数的最后一个)
那么后一部分和是没有的。。因为并没有哪一个块被$a_n$所“切开”
问题4:总和
由以上叙述 总和
- $ans=psum(t),when \sum_{k=1}^{t}f(k) = n$
- $ans=psum(t)+(t+1)\cdot (n-sum(t)),when \sum_{k=1}^{t}f(k) < n$
优化1
找$t$
由于和式满足二分条件(递增)
那我们直接二分查找这个$t$
复杂度$O(\log n)$
优化2
我们会发现 $sum(i)$ 算起来很慢 如果乱搞的话是 $O(n)$ 的 前面已经说过了这样的复杂度不行
观察一下 $f(x)$ 的定义
它计算了数i在二进制下末尾0的个数
那我们换一种sum(x)的表示法
我们可以先固定二进制位下k-1个末尾0 并固定第k位为1
其他位随意组合 但最终值不能超过x
那么这样就统计了所有f(x)=k的和
表示为 $\lfloor \frac{x}{2^k} \rfloor$
$k$从1枚举到最大 那么
$sum(x)=\sum_{k=1}^{MAX}\lfloor \frac{x}{2^k} \rfloor$
复杂度是$O(\log n)$
优化3
同样的$psum(x)$算起来也很慢 乱搞也是$O(n)$的
上面我们说过一个规律
出现i次的数是$2^{i-1}$ (1 3 5 7 9 …)
1 3 5 7 9 … 是等差数列 和可以用$O(1)$公式法搞定
那么重新叙述$psum(x)$ 我们可以先把出现1次的数全部加起来 然后把出现2次的 出现3次的 出现4次的数都加起来 由于出现i次的数最小是$2^{i-1}$嘛 那么这边求和也是$O(\log n)$的
问题是等差数列的末项是啥
假设出现i次的数的最后一个数是$2^{i-1}*(2p-1)$其中$p\in N^+$那么这个数满足
- $p$最大
- $2^{i-1}*(2p-1)\le x$
这个还是挺方便解的 移项完了是 $p \le \frac{\frac{x}{2^{i-1}}+1}{2}$ 那$p$直接取$\lfloor\frac{\lfloor\frac{x}{2^{i-1}}\rfloor+1}{2}\rfloor$就行了
然后求一波等差数列和 (首项+尾项)*(项数))/2
$S=2^{i-1}\cdot p\cdot p$
完了我们回去算 $psum(x)=\sum_{i=1}^{MAX}{i\cdot 2^{i-1}\cdot p\cdot p}$
复杂度$O(\log n)$
注意
我们一开始剔掉了$a_1$ 不要忘了把它加回来
复杂度
答案表达式是这样的:
- $ans=psum(t),when \sum_{k=1}^{t}f(k) = n$
- $ans=psum(t)+(t+1)\cdot (n-sum(t)),when \sum_{k=1}^{t}f(k) < n$
在情况1下 就是$psum(x)$的复杂度$O(\log n)$
在情况2下 复杂度是$O(\log n\cdot\log n)$
所以总的复杂度是 $O(\log n\cdot \log n)$
结合一下数据规模 嗯 应该不会炸了…
ps:由于这题的时限卡的有点紧(标程好像是用阿贝尔变换做的)多校现场赛有人用$O(\log n\cdot \log n)$ 的算法T了 所以最好在写的时候注意下常数优化
参考代码
1 |
|