min25筛学习小结

前言

这玩意儿听说其大名好久了,但一直没学。
那么这次就来补补课。

好久没写博客了(其实是懒得写

埃氏筛法

这个有什么用呢,其实也只是一个思想。
埃氏筛法其实就是用来筛质数的一个朴素方法(弱鸡我至今还在用

过程大概就是:
从2开始,把以后的2的倍数都打上标记,然后再找下一个没被标记的。
找到3,把以后的3的倍数都打上标记,然后再找下一个没被标记的。
……

就没了。

min25筛

用处

这个东东其实就是用来算某个积性函数 f ( i ) f(i) f(i)的前缀和。
可以在 O ( n 3 4 l o g   n ) O(\frac{n^{\frac 3 4}}{log\ n}) O(log nn43)的复杂度内计算。

听说有一个新版的min25筛,时间是 O ( n 2 3 ) O(n^{\frac 2 3}) O(n32)的?
感觉好神奇啊。(正是因为学不会)

条件

这个条件其实有两个:
1、 f ( i c ) f(i^c) f(ic)可以快速求,其中i为质数。
2、 f ( i ) f(i) f(i)可以表示成一个关于i的多项式,其中i为质数。

具体做法

我们发现直接做不好做。
然后min25就教我们去分成两部分做:

质数

我们考虑原来的 f ( i ) f(i) f(i)
这样求这个式子 ∑ i = 1 n [ i ∈ P ] f ( i ) \sum_{i=1}^n[i\in P]f(i) i=1n[iP]f(i)其中P为质数。
然后我们发现这样也不好做。

那换个思路,先求个简单的(才怪
设一个新的f表示 f ( i ) = i k f(i)=i^k f(i)=ik这个k是一个自然数。
∑ i = 1 n [ i ∈ P ] f ( i ) \sum_{i=1}^n[i\in P]f(i) i=1n[iP]f(i)
假如我们求出来了之后,有什么用呢?
由于原来的f可以表示成多项式,那么原来的多项式就可以表示成现在求的f乘上一个系数a即可。
(这玩意理解了本蒟蒻半天,还是太菜了)
注意,这玩意儿很重要,千万不可忽略!

而这玩意怎么求呢?
我们再引入一个关键的数组:
g ( n , j ) = ∑ i = 2 n [ i ∈ P 或 m p [ i ] > p [ j ] ] f ( i ) g(n,j)=\sum_{i=2}^n[i\in P或mp[i]>p[j]]f(i) g(n,j)=i=2n[iPmp[i]>p[j]]f(i)
其中 m p [ i ] mp[i] mp[i]表示i的最小质因子, p [ i ] p[i] p[i]表示从小到大的第i个质数。

然后这玩意是什么意思呢?
也就是求出1到n中, i i i为质数或 i i i的最小质因子大于当前第 j j j个质数的 f ( i ) f(i) f(i)
仔细观察其中微妙的地方,发现这玩意不就是对应着埃氏筛法吗?
更细来说就是:
g ( n , j ) g(n,j) g(n,j)表示当前筛法筛了j次质数后,未被标记的 f ( i ) f(i) f(i)的和
那么反过来讲,就变成:
∑ i = 1 n [ i ∈ P ] f ( i ) 等 价 于 求 g ( n , ∣ P n ∣ ) \sum_{i=1}^n[i\in P]f(i)等价于求g(n,|Pn|) i=1n[iP]f(i)g(n,Pn)
其中 ∣ P n ∣ |Pn| Pn表示小于 n n n的质数集合大小。
(像“求出g就求出f”这种废话就不说了吧)

所以说转了半天,我们终于得到一个确切的目标——求g

怎么求呢?

首先我们看到初始化。
假设我们要求 g ( n , 0 ) g(n,0) g(n,0)这个东东,我们发现,只要k确定之后,那么这玩意就是自然数幂和。
什么?你说你不会?
戳这里学学
然后我们就考虑从 g ( n , 0 ) g(n,0) g(n,0)往后面推。
那么假设从 g ( n , j − 1 ) — — > g ( n , j ) g(n,j-1)——>g(n,j) g(n,j1)>g(n,j)
这就意味着把原来那个埃氏筛法的数组中又筛掉了当前最小质因子为 p [ j ] p[j] p[j]的所以数。

然后我们分两类:

1、关于 p [ j ] 2 p[j]^2 p[j]2(也就是筛掉的数里面最小的那个) > n >n >n
这就意味着之后都是不会再从数组中筛掉任何数,所以贡献不会变。
转移:
g ( n , j ) = g ( n , j − 1 ) g(n,j)=g(n,j-1) g(n,j)=g(n,j1)

2、关于 p [ j ] 2 p[j]^2 p[j]2(不再解释了吧) < = n <=n <=n
这意味着我们要从数组中筛掉很多个数。
而这些数由于有共同的质因子,所以我们可以快速求出其答案:
转移:
g ( n , j ) = g ( n , j − 1 ) − A n s g(n,j)=g(n,j-1)-Ans g(n,j)=g(n,j1)Ans
等等,你这个 A n s Ans Ans是什么意思?
其实就是容斥的贡献。

首先我们发现,是要减去后面被筛掉的关于最小质因数为 p [ j ] p[j] p[j] f f f值。
且由于 f f f是一个积性函数。
所以我们可以把 f ( p [ j ] ) f(p[j]) f(p[j])当做系数先提取出来最前面,再算后面对应的位置。
由于我们要筛掉的是后面除 p [ j ] p[j] p[j]以外最小质因数都大于等于 p [ j ] p[j] p[j]的数。
所以一边容斥的结果是:
f ( p [ j ] ) ∗ g ( ⌊ n p [ j ] ⌋ , j − 1 ) f(p[j])*g(\lfloor \frac n {p[j]}\rfloor,j-1) f(p[j])g(p[j]n,j1)

然鹅这样会算多,由于g它构造出来关于前面的质数也算进去了。
所以我们删掉 g ( p [ j ] − 1 , j − 1 ) g(p[j]-1,j-1) g(p[j]1,j1)即可。

总贡献
A n s = f ( p [ j ] ) ∗ ( g ( ⌊ n p [ j ] ⌋ , j − 1 ) − g ( p [ j ] − 1 , j − 1 ) ) Ans=f(p[j])*(g(\lfloor \frac n {p[j]}\rfloor,j-1)-g(p[j]-1,j-1)) Ans=f(p[j])(g(p[j]n,j1)g(p[j]1,j1))

综上所述:
g ( n , j ) = { g ( n , j − 1 ) − f ( p [ j ] ) ∗ ( g ( ⌊ n p [ j ] ⌋ , j − 1 ) − g ( p [ j ] − 1 , j − 1 ) ) g ( n , j − 1 ) g(n,j)=\{^{g(n,j-1)}_{g(n,j-1)-f(p[j])*(g(\lfloor \frac n {p[j]}\rfloor,j-1)-g(p[j]-1,j-1))} g(n,j)={g(n,j1)f(p[j])(g(p[j]n,j1)g(p[j]1,j1))g(n,j1)
然鹅我们实际操作其实 p [ j ] 2 > n p[j]^2>n p[j]2>n的式子并不需要,所以我们简洁地写成这样:

g ( n , j ) = g ( n , j − 1 ) − f ( p [ j ] ) ∗ ( g ( ⌊ n p [ j ] ⌋ , j − 1 ) − ∑ i = 1 j − 1 f ( p [ i ] ) ) g(n,j)=g(n,j-1)-f(p[j])*(g(\lfloor \frac n {p[j]}\rfloor,j-1)-\sum_{i=1}^{j-1}f(p[i])) g(n,j)=g(n,j1)f(p[j])(g(p[j]n,j1)i=1j1f(p[i]))

然后我们求出的是简单版的f值。
而复杂版的就是可以求多次k,然后前面乘上一个系数a。(根据题目而定)

讲了长篇大论,终于讲完第一步了。
接下来就是直接求完全部。

全部

递归版

我们接下来是利用上面求出来的g和思路,来看一下能不能容斥一下把剩下的合数也给求了。
我们同样设一个很类似的数组 s ( n , j ) s(n,j) s(n,j)
s ( n , j ) = ∑ i = 2 n [ m p [ i ] > = p [ j ] ] F ( i ) s(n,j)=\sum_{i=2}^n[mp[i]>=p[j]]F(i) s(n,j)=i=2n[mp[i]>=p[j]]F(i)
(注意,这里的 F ( i ) F(i) F(i)是直接表示一开始要求的积性函数,如果理解成简单版的也不是不行。)

而这玩意与上面的g相比其实就是把中间的质数都给标记掉了和加了个等号罢。
然后你会发现,答案即为: s ( n , 1 ) + F ( 1 ) s(n,1)+F(1) s(n,1)+F(1)
那么我们考虑转移:

首先对于质数的贡献:
由于我们要求除去之前那些质数的贡献,那么这个贡献可以利用上面的g来做:
g ( n , ∣ P n ∣ ) − ∑ i = 1 j − 1 f ( p [ i ] ) g(n,|Pn|)-\sum_{i=1}^{j-1}f(p[i]) g(n,Pn)i=1j1f(p[i])
这个f是构造的数组。
或者说,后面的那坨求和可以替换成:
g ( n , ∣ P n ∣ ) − g ( p [ j − 1 ] , j − 1 ) g(n,|Pn|)-g(p[j-1],j-1) g(n,Pn)g(p[j1],j1)
这样就把大于等于 p [ j ] p[j] p[j]的所以质数都扔进去了。

其次对于合数的贡献:
首先,我们可以枚举最小的质因数。
然后再枚举当前质因数的幂。
再根据f为积性函数,可以来算贡献:
∑ k = j ∣ P n ∣ [ p [ k ] 2 < = n ] ∑ e = 1 p [ k ] e + 1 < = n ( F [ p [ k ] e ] ∗ s ( ⌊ n p [ k ] e ⌋ , k + 1 ) + F [ p [ k ] e + 1 ] ) \sum_{k=j}^{|Pn|}[p[k]^2<=n]\sum_{e=1}^{p[k]^{e+1}<=n}(F[p[k]^e]*s(\lfloor \frac n {p[k]^e}\rfloor,k+1)+F[p[k]^{e+1}]) k=jPn[p[k]2<=n]e=1p[k]e+1<=n(F[p[k]e]s(p[k]en,k+1)+F[p[k]e+1])
所以总柿子就变成:
s ( n , j ) = g ( n , ∣ P n ∣ ) − g ( p [ j − 1 ] , j − 1 ) + ∑ k = j ∣ P n ∣ [ p [ k ] 2 < = n ] ∑ e = 1 p [ k ] e + 1 < = n ( F [ p [ k ] e ] ∗ s ( ⌊ n p [ k ] e ⌋ , k + 1 ) + F [ p [ k ] e + 1 ] ) s(n,j)=g(n,|Pn|)-g(p[j-1],j-1)+\sum_{k=j}^{|Pn|}[p[k]^2<=n]\sum_{e=1}^{p[k]^{e+1}<=n}(F[p[k]^e]*s(\lfloor \frac n {p[k]^e}\rfloor,k+1)+F[p[k]^{e+1}]) s(n,j)=g(n,Pn)g(p[j1],j1)+k=jPn[p[k]2<=n]e=1p[k]e+1<=n(F[p[k]e]s(p[k]en,k+1)+F[p[k]e+1])
或者写成这样(实测这样好打)

s ( n , j ) = g ( n , ∣ P n ∣ ) − ∑ i = 1 j − 1 f ( p [ i ] ) + ∑ k = j ∣ P n ∣ ∑ e = 1 p [ k ] e + 1 < = n ( F [ p [ k ] e ] ∗ s ( ⌊ n p [ k ] e ⌋ , k + 1 ) + F [ p [ k ] e + 1 ] ) s(n,j)=g(n,|Pn|)-\sum_{i=1}^{j-1}f(p[i])+\sum_{k=j}^{|Pn|}\sum_{e=1}^{p[k]^{e+1}<=n}(F[p[k]^e]*s(\lfloor \frac n {p[k]^e}\rfloor,k+1)+F[p[k]^{e+1}]) s(n,j)=g(n,Pn)i=1j1f(p[i])+k=jPne=1p[k]e+1<=n(F[p[k]e]s(p[k]en,k+1)+F[p[k]e+1])
注意,上面前面的f是我们构造的f,后面两个F是原来的函数。
然后这个就是递归版的。

注意,上面那个 ∣ P n ∣ |Pn| Pn其实并不需要枚举完,枚举到 n \sqrt n n 即可。因为如果还大些的话是没有值的。

递推版

其实这两个玩意儿没什么大的区别,只是设的状态有微小的差别导致计算时一个递推一个递归。
其意义其实比较诡异,似乎要根据具体题目来看。
吃饱了撑死也比什么也没吃饿死好

这次设的 s ( n , j ) s(n,j) s(n,j)就真的和上面的 g ( n , j ) g(n,j) g(n,j)特别类似了。

s ( n , j ) = ∑ i = 2 n [ m p [ i ] > = p [ j ] ∣ i ∈ P ] f ( i ) s(n,j)=\sum_{i=2}^n[mp[i]>=p[j]|i \in P]f(i) s(n,j)=i=2n[mp[i]>=p[j]iP]f(i)
递推柿子就简单列出来吧,反正推理和上面是基本一样的。

s ( n , j ) = s ( n , j + 1 ) + f ( p [ j ] ) + ∑ e = 1 p [ j ] e + 1 < = n ( f [ p [ j ] e ] ∗ s ( ⌊ n p [ j ] e ⌋ , j + 1 ) + f [ p [ j ] e + 1 ] ) s(n,j)=s(n,j+1)+f(p[j])+\sum_{e=1}^{p[j]^{e+1}<=n}(f[p[j]^e]*s(\lfloor \frac n {p[j]^e}\rfloor,j+1)+f[p[j]^{e+1}]) s(n,j)=s(n,j+1)+f(p[j])+e=1p[j]e+1<=n(f[p[j]e]s(p[j]en,j+1)+f[p[j]e+1])
然后加个小优化,当 p [ j ] 2 > n p[j]^2>n p[j]2>n
加上 ∑ p [ k ] 2 > n f [ p [ k ] ] \sum_{p[k]^2>n}f[p[k]] p[k]2>nf[p[k]]然后退出即可。

然后就没啦。

代码实现

我们发现,我们求g数组时,n比较大,所以我们考虑离散化这玩意。具体操作就是分成小于和大于 n \sqrt n n 的两部分,然后分别用两个数组来维护。由于需要使用到的n只有 n \sqrt n n 个,所以我们可以暴力维护。
其他小细节看代码吧。
Loj#6035简单的函数
luoguP5325 【模板】Min_25筛
jzoj5025. 【NOI2017模拟3.19】Sum
(话说我打完板子回来还发现了好几个锅。果然只口胡不写代码真的布星)
在这里插入图片描述

参考资料

https://www.cnblogs.com/cjyyb/p/9185093.html
https://blog.csdn.net/gmh77/article/details/88142031
https://www.cnblogs.com/zhoushuyu/p/9187319.html
https://www.cnblogs.com/GuessYCB/p/10061411.html
https://blog.csdn.net/Cold_Chair/article/details/82533036
https://blog.csdn.net/jokerwyt/article/details/82150472
https://blog.csdn.net/XianHaoMing/article/details/80397777
https://blog.csdn.net/A1847225889/article/details/105849185
有一说一,其实自己看还是有点困难的。好几个地方都是问古爷和597懂的。
还是太菜了QWQ

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值