洛谷P5325 【模板】Min_25筛

链接

https://www.luogu.org/problemnew/show/P5325

学习资料

L o l i c o n A u t o m a t o n LoliconAutomaton LoliconAutomaton出了一道 m i n _ 25 min\_25 min_25筛题,于是我就开始学 m i n _ 25 min\_25 min_25
推荐学习资料:
https://www.cnblogs.com/zhoushuyu/p/9187319.html
https://www.cnblogs.com/cjyyb/p/9185093.html
在这里插入图片描述
两篇一起看食用效果更佳

Min_25筛

日本人的脑洞永远是我们无法想象的大
这算法完全不懂是怎么想到的,只知道太强了,%就完事了
在这里插入图片描述在这里插入图片描述在这里插入图片描述

适用条件

对于一个积性函数 f ( x ) f(x) f(x),当 x ∈ p r i m e x\in prime xprime f ( x ) f(x) f(x)的表达式是多项式,而且 f ( p c ) f(p^c) f(pc)可以快速计算,那么这玩意就可以用 m i n _ 25 min\_25 min_25筛来做

第一步,求自变量为质数的 f ( x ) f(x) f(x)之和

现在对于这个多项式函数 f ( x ) f(x) f(x),我把它每一项拆出来单独做(最后再加起来),设 F ( x ) = x k F(x)=x^k F(x)=xk,现在我们要对所有满足 x ∈ p r i m e x\in prime xprime x x x F ( x ) F(x) F(x)的和
注意,这里的 F ( x ) = x k F(x)=x^k F(x)=xk是对所有的 x x x成立的,也就是说这个函数并不是原来的那个积性函数了,但是在质数处的取值是和原来的函数一样的,既然现在只求质数处的函数值之和,那么也就不会影响最后结果的正确性
P j P_j Pj为第 j j j大的质数( P 1 = 2 , P 2 = 3 , P 3 = 5... P_1=2,P_2=3,P_3=5... P1=2,P2=3,P3=5...
g ( n , j ) g(n,j) g(n,j)为把最小质因子 &lt; = P j &lt;=P_j <=Pj的那些合数去掉之后,剩下的数的 F ( x ) F(x) F(x)之和
严谨的写一下: g ( n , j ) = ∑ x = 2 n F ( x ) [ ( x ∈ p i r m e ) ∨ ( m i n p ( x ) &gt; P j ) ] g(n,j)=\sum_{x=2}^nF(x)[(x\in pirme)\vee (minp(x)&gt;P_j)] g(n,j)=x=2nF(x)[(xpirme)(minp(x)>Pj)]
这个东西可以用来递推,最终求出 g ( n , n u m ) g(n,num) g(n,num)就是答案,其中 n u m num num表示不大于 n \sqrt n n 的质数的总数(因为每个合数都会被 ≤ n \leq \sqrt n n 的质数筛掉)
为了便于理解,我举个例子,假设 F ( p ) = p F(p)=p F(p)=p,那么 f ( p ) f(p) f(p)序列就是 f ( 2 ) = 2 , f ( 3 ) = 3 , f ( 4 ) = 4 , f ( 5 ) = 5 f(2)=2,f(3)=3,f(4)=4,f(5)=5 f(2)=2,f(3)=3,f(4)=4,f(5)=5,那么
g ( 5 , 0 ) = f ( 2 ) + f ( 3 ) + f ( 4 ) + f ( 5 ) g(5,0)=f(2)+f(3)+f(4)+f(5) g(5,0)=f(2)+f(3)+f(4)+f(5)
g ( 5 , 1 ) = f ( 3 ) + f ( 5 ) g(5,1)=f(3)+f(5) g(5,1)=f(3)+f(5)
g ( 5 , 2 ) = f ( 5 ) g(5,2)=f(5) g(5,2)=f(5)
g ( 5 , 3 ) = 0 g(5,3)=0 g(5,3)=0
可以发现随着 j j j的增大,这就是素数筛的过程,不停的用素数把某些项去掉
现在定义完了,来看看这东西怎么算
显然当 j &gt; s q r t ( n ) j&gt;sqrt(n) j>sqrt(n)时, g ( n , j ) = g ( n , j − 1 ) g(n,j)=g(n,j-1) g(n,j)=g(n,j1),因为 P j P_j Pj这个素数没有筛掉任何一项
j ≤ s q r t ( n ) j\leq sqrt(n) jsqrt(n)时,看看它筛掉了哪些?显然是那些最小质因子等于 P j P_j Pj的数字,假设这个数除以 P j P_j Pj之后得到的商为 x x x,那么 P j ≤ x P_j\leq x Pjx x P j ≤ n xP_j\leq n xPjn,可以得到 P j ≤ x ≤ ⌊ n P j ⌋ P_j\leq x \leq \lfloor\frac{n}{P_j}\rfloor PjxPjn,再根据 F F F的完全积性,得到 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(Pj)(g(Pjn,j1)i=1j1F(Pi))
可以发现这里的递推种 g ( x , y ) g(x,y) g(x,y)的第一个自变量 x x x都是 ⌊ n k ⌋ \lfloor\frac{n}{k}\rfloor kn的形式,所以很幸运我们的 x x x的取值只有根号种
总结一下
g ( n , j ) = { g ( n , j − 1 ) P j &gt; n g ( n , j − 1 ) − F ( P j ) ( g ( ⌊ n P j ⌋ , j − 1 ) − ∑ i = 1 j − 1 F ( P i ) ) P j ≤ n g(n,j)= \begin{cases} g(n,j-1) &amp;&amp;P_j&gt;\sqrt n \\ g( n , j-1 )-F(P_j)(g(\lfloor\frac{n}{P_j}\rfloor,j-1)-\sum_{i=1}^{j-1}F(P_i)) &amp;&amp; P_j \leq \sqrt n \\ \end{cases} g(n,j)={g(n,j1)g(n,j1)F(Pj)(g(Pjn,j1)i=1j1F(Pi))Pj>n Pjn
这玩意具体怎么算呢?
首先我需要一个 F F F函数的前缀和
然后观察发现 j j j在递推过程中每次减 1 1 1,那既然如此我像背包那样倒过来循环的话,就能少一维空间,一维数组就可以了
大体步骤就是:
1.初始化 g ( ⌊ n i ⌋ , 0 ) g(\lfloor\frac{n}{i}\rfloor,0) g(in,0)
2.筛素数并计算 F F F的前缀和
3.递推算 g ( l f l o o r n i ⌋ , j ) g(lfloor\frac{n}{i}\rfloor,j) g(lfloorin,j)

第二步,算答案

刚才算了质数的,现在算合数的并且合并出答案
S ( n , j ) S(n,j) S(n,j)表示最小质因子 ≥ P j \geq P_j Pj ≤ n \leq n n x x x的函数值之和
S ( n , j ) = ∑ i = 2 n f ( i ) [ m i n p ( i ) ≥ P j ] S(n,j)=\sum_{i=2}^nf(i)[minp(i)\geq P_j] S(n,j)=i=2nf(i)[minp(i)Pj]
(注意这里变成 f f f了,刚才一直在讨论 F F F,不要搞混)
然后分质数、合数分开算
质数的贡献就是 g ( n , n u m ) − ∑ i = 1 j − 1 f ( i ) g(n,num)-\sum_{i=1}^{j-1}f(i) g(n,num)i=1j1f(i),这很容易理解,就是全部的减去太小的
合数的贡献就是 ∑ k = j p k 2 ≤ n ∑ e = 1 p k e + 1 ≤ n S ( ⌊ n P k e ⌋ , k + 1 ) f ( P k e ) + f ( p k e + 1 ) \sum_{k=j}^{pk^2\leq n}\sum_{e=1}^{p_k^{e+1}\leq n}S(\lfloor\frac{n}{P_k^e}\rfloor,k+1)f(P_k^e)+f(p_k^{e+1}) k=jpk2ne=1pke+1nS(Pken,k+1)f(Pke)+f(pke+1),这部分啥意思呢?既然我只算合数的,那我先枚举最小质因数 P k P_k Pk,并枚举其幂次 e e e,剩下的部分可能是大小不超过 ⌊ n P k e ⌋ \lfloor\frac{n}{P_k^e}\rfloor Pken且约数只由大于第 k k k个质数的质数组成,也可能包含这个质数的幂(为了不重复计算只需要考虑一次幂), p k e + 1 ≤ n p_k^{e+1}\leq n pke+1n这个不等式的含义是,表面上先保证 f ( p k e + 1 ) f(p_k^{e+1}) f(pke+1)合法,其次我可以写出 p k e p k + 1 ≤ n p_k^ep_{k+1}\leq n pkepk+1n,当这个不等式不满足的时候, S ( ⌊ n P k e ⌋ , k + 1 ) S(\lfloor\frac{n}{P_k^e}\rfloor,k+1) S(Pken,k+1)显然为 0 0 0,也就没必要算下去
递推式写出来如下:
S ( n , j ) = g ( n , n u m ) − ∑ i = 1 j − 1 f ( i ) + ∑ k = j p k 2 ≤ n ∑ e = 1 p k e + 1 ≤ n S ( ⌊ n P k e ⌋ , k + 1 ) f ( P k e ) + f ( p k e + 1 ) S(n,j)=g(n,num)-\sum_{i=1}^{j-1}f(i)+\sum_{k=j}^{pk^2\leq n}\sum_{e=1}^{p_k^{e+1}\leq n}S(\lfloor\frac{n}{P_k^e}\rfloor,k+1)f(P_k^e)+f(p_k^{e+1}) S(n,j)=g(n,num)i=1j1f(i)+k=jpk2ne=1pke+1nS(Pken,k+1)f(Pke)+f(pke+1)
这一部分的实现非常直接,就是个递归,连记忆化都没有

时间复杂度

参考:
https://www.luogu.org/blog/user54214/solution-p5325
貌似大家口径不是很统一,说法有两种,要么 O ( n 1 − ϵ ) O(n^{1-\epsilon}) O(n1ϵ),要么 O ( n 3 4 l o g n ) O(\frac{n^{\frac{3}{4}}}{log_n}) O(lognn43)
反正我是不会分析…

代码

#include <bits/stdc++.h>
#define maxn 200010
#define mod 1000000007ll
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
typedef long long ll;
struct EasyMath
{
    ll prime[maxn];
    bool mark[maxn];
    ll fastpow(ll a, ll b, ll c)
    {
        ll t(a%c), ans(1ll);
        for(;b;b>>=1,t=t*t%c)if(b&1)ans=ans*t%c;
        return ans;
    }
    void get_prime(ll N)
    {
        ll i, j;
        for(i=2;i<=N;i++)mark[i]=false;
        *prime=0;
        for(i=2;i<=N;i++)
        {
            if(!mark[i])prime[++*prime]=i;
            for(j=1;j<=*prime and i*prime[j]<=N;j++)
            {
                mark[i*prime[j]]=true;
                if(i%prime[j]==0)break;
            }
        }
    }
}em;
struct min_25   //f(p^k)=p^k * (p^k - 1)
{
    ll n, sf1[maxn], sf2[maxn], g1[maxn], g2[maxn], id1[maxn], id2[maxn], lis[maxn], _m;
    vector<ll> index;
    inline ll getid(ll x)
    {
        return x<=_m ? id1[x] : id2[n/x];
    }
    void calcg()
    {
        ll i, j;
        //初始化g
        ll _2=em.fastpow(2,mod-2,mod), _6=em.fastpow(6,mod-2,mod);
        for(i=1;i<=n;i=j+1)
        {
            j=n/(n/i);
            lis[++*lis]=n/i;
            if(n/i<=_m)id1[n/i]=*lis;
            else id2[n/(n/i)]=*lis;
            ll t=(n/i)%mod;
            g1[*lis] = ( t*(t+1)%mod*_2 -1 )%mod;
            g2[*lis] = ( t*(t+1)%mod*(2*t+1)%mod*_6 -1 )%mod;
            //g[j]=\sum_{i=2}^j f(j)
        }
        //求f(p_j)的前缀和
        em.get_prime(_m);
        for(i=1;i<=*em.prime;i++)
        {
            auto p=em.prime[i];
            sf1[i]=(sf1[i-1]+p)%mod;
            sf2[i]=(sf2[i-1]+p*p)%mod;
        }
        //求g
        for(j=1;j<=*em.prime;j++)
        {
            auto p=em.prime[j];
            for(ll k=1;k<=*lis;k++)
            {
                if(p*p>lis[k])break;
                ll t = getid(lis[k]/p);
                (g1[k]-=p*(g1[t]-sf1[j-1]))%=mod;
                (g2[k]-=p*p%mod*(g2[t]-sf2[j-1]))%=mod;
            }
        }
    }
    ll S(ll x, ll y)
    {
        if(em.prime[y]>x)return 0;
        auto t=getid(x);
        auto ans=(g2[t]-g1[t])-(sf2[y-1]-sf1[y-1]);
        ans%=mod;
        for(ll i=y;i<=*em.prime and em.prime[i]*em.prime[i]<=x;i++)
        {
            for(auto p1=em.prime[i], p2=p1*p1;p2<=x;p1=p2, p2*=em.prime[i])
            {
                (ans+=S(x/p1,i+1)*(p1*(p1-1)%mod)%mod+p2%mod*(p2%mod-1)%mod)%=mod;
            }
        }
        return ans;
    }
    ll run(ll N)
    {
        _m=sqrt(N);
        cl(g1), cl(g2);
        cl(sf1), cl(sf2);
        n=N;
        calcg();
        return (S(N,1)%mod+1+mod)%mod;
    }
}m25;
int main()
{
    ll N;
    cin>>N;
    cout<<m25.run(N);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值