luoguP6028算术 题解(推柿子+整除分块+调和级数)
题目
大意很好理解,给你一个肥常奇怪的柿子,求它的值。
30分做法(暴力):
显然通过题目给出的柿子本身就可以O(n)求f[i]然后再O(n)求和,可以得到一个O(n2)的算法。
60分做法(大力推柿子):
通过一番鬼斧神工般的操作,成功把原柿子转化成d≤n∑d∣n1d
转化过程如下:(正着推需要极其高超敏锐的数学直觉,所以我们倒着推)
最难的一步:∑d∣n1d=pcnt∏i=1(1p0i+1p1i+1p2i+……+1pcntii)
这里的pcnt表示n所分解成的质因数的个数,pi表示第i个质数,cnti表示第i个质数在n中有几个。
正确性:每一个约数一定都是n的质因子中选某些质因子的某些次方拼成的,满足一一对应,等式前面的是所有约数和,等式后面是所有质因子所有次方的可能组合方式的和(根据乘法分配率)。
接着往下推吧:
pcnt∏i=1(1p0i+1p1i+1p2i+……+1pcntii)=pcnt∏i=1p0i∗(1−1pcnti+1i)1−1pi
这一步是等比数列求和公式。
下面:
pcnt∏i=1p0i∗(1−1pcnti+1i)1−1pi=pcnt∏i=1pcnti+1i−1pcnti+1ipi−1pi
pcnt∏i=1pcnti+1i−1pcnti+1ipi−1pi=pcnt∏i=1pcnti+1−1pcnti∗(pi−1)=pcnt∏i=1pcnti+1−1pcnti+1−pcnti
到这里,就是题目中给的柿子了,推完了……吗?
发现这个柿子还是没有优化到O(n)(对于每一个n需要枚举它的所有约数,这是O(n√n)的),还需要进一步优化
n∑i=1∑d∣i1d=n∑d=1i≤n∑d∣i1d
这一步我也不太会证,大概就是从实际意义上去考虑,枚举每一个i,算一遍能整除它的d,和枚举每一个d,算一遍它能整除的i,这样每一对d~i被计算的次数都是一样的,所以正确性应该没啥问题……
然后就是最后一步辣:
n∑d=1i≤n∑d∣i1d=n∑d=1⌊nd⌋∗1d
刚才那个柿子的第二个∑显然就是找1~n里面有几个能被d整除的数,这个数量就是⌊nd⌋
至于证明(我自己口胡的):⌊nd⌋这个柿子的结果等于在n以内能被d整除的最大的数除以d的结果,就是n以内能被d整除的数的个数
这就是O(n)的了,直接跑一遍就OK
100分做法:(整除分块+调和级数)
考虑继续优化这个柿子:n∑d=1⌊nd⌋∗1d
把这个柿子拆成两半考虑:
n∑d=1⌊nd⌋∗1d=n∑d=1⌊nd⌋∗n∑d=11d
然后就显然发现前一半是整除分块,后一半是调和级数
恶补下这两个知识点:
- 整除分块:
要求形如n∑d=1⌊nd⌋的柿子可以O(√n)求。
运用了在n以内,n除以很多数结果是一样的可以直接合并起来算,然后这些合并后的数大概级别是√n的,把每一堆结果相同的d归为「一块」,只算一遍。
具体操作:
根据定义对于每一个块的起点l和终点r,有⌊nl⌋=⌊nr⌋,而r作为这一块的最后一项,有性质⌊n⌊nr⌋⌋=r,所以从左到右遍历时候,对于每一个l,直接计算(r−l+1)∗⌊nl⌋然后把l跳到r+1,进到下一个块,就完了。 - 调和级数:
调和级数是形如1+12+13+14+……的一个发散的无穷级数。
上面的柿子中后一半n∑d=11d就是个调和级数的前n项。
关于调和级数有这样一个柿子:1+1/2+1/3+1/4+...1/n=ln(n+1)+r,r是欧拉常数约等于0.5772156649(这个柿子是欧拉推的,不是我)
因为欧拉常数是个近似值,它会卡精度,所以我们可以预处理前1e7个答案的前缀和,剩下的用这个柿子求。
所以最后的答案是:n∑d=1⌊nd⌋∗1d=∑每一个块的l,r(r−l+1)∗⌊nl⌋∗(1l∗1l+1∗1l+2∗……∗1r)
对于每一个块,计算(r−l+1)∗⌊nl⌋∗(sum[r]/sum[l−1])的值,然后累加起来就是答案了。
ps:
通过这道题我昨天花了好长时间发现一个没啥用的小规律:
∑d∣ndn=∑d∣n1d
确实是对的可以证。
我是在看了题解后尝试把原柿子正着推回去的时候,经过另一个等比数列求和(1+pi+p2i+p3i+……的和),变成了∑d∣ndn,我发现自己推柿子的过程没问题,所以猜测这俩柿子是相等的,然后发现真是相等的。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步