• 线性筛法 & 洛谷P4449 于神之怒加强版


    筛质数

    基础的。

        // v 标记是否为合数,p 存储质数
        for (int i=2; i<=N; ++i) {
            v[i]||(p[++cnt]=i);
            for (int j=1,t; j<=cnt&&(t=p[j]*i)<=N; ++j) {
                v[t]=1;
                if (i%p[j]==0) break;
            }
        }
    

    原理:每个合数 \(t\) 只被其最小质因子 \(p_j\) 筛一次。

    \(i\) 的最小质因子为 \(p_{j_0}\)
    \(j\le j_0\) 时,\(p_j\)\(t=p_j\times i\) 的最小质因子,因此用 \(p_j\) 筛掉 \(t\)
    \(j>j_0\) 时,\(p_{j_0}\)\(t\) 的最小质因子,不应再用 \(p_j\) 去筛,直接 break 掉就好了。

    根据原理,上述筛法的时间复杂度是线性的,故称为线性筛。

    筛欧拉函数 \(\varphi\)

    • 对于质数 \(i\)\(\varphi(i)=i-1\)

    • 对于合数 \(t=p_j\times i\)(其中 \(p_j\)\(t\) 的最小质因子):
      \(p_j\mid i\),则 \(\varphi(t)=p_j\times\varphi(i)\)
      \(p_j\nmid i\),则 \(\varphi(t)=\varphi(p_j)\times\varphi(i)\)

        phi[1]=1;
        for (int i=2; i<=N; ++i) {
            v[i]||(p[++cnt]=i,phi[i]=i-1);
            for (int j=1; j<=cnt&&(t=p[j]*i)<=N; ++j) {
                v[t]=1;
                if (i%p[j]==0) { phi[t]=p[j]*phi[i]; break; }
                phi[t]=phi[p[j]]*phi[i];
            }
        }
    

    筛莫比乌斯函数 \(\mu\)

    同上,简单讨论一下即可。

    • \(i\in\mathbb P\Longrightarrow\mu(i)=-1\)。(用 \(\mathbb P\) 表示质数集)
    • \(p_j\mid i\Longrightarrow \mu(t)=0\)
      \(p_j\nmid i\Longrightarrow \mu(t)=\mu(p_j)\times\mu(i)=-\mu(i)\)
        mu[1]=1;
        for (int i=2; i<=N; ++i) {
            v[i]||(p[++cnt]=i,mu[i]=-1);
            for (int j=1; j<=cnt&&(t=p[j]*i)<=N; ++j) {
                v[t]=1;
                if (i%p[j]==0) break;
                mu[t]=-mu[i];
            }
        }
    

    筛因数个数 \(d\)

    计算公式:设 \(n=\prod p_i^{k_i}\),则 \(d(n)=\prod(k_i+1)\)
    \(p_1\) 为最小质因子,令 \(f(n)=k_1+1\)

    • \(i\in\mathbb P\Longrightarrow f(i)=2,d(i)=2\)
    • \(p_j\mid i\Longrightarrow f(t)=f(i)+1,d(t)=\dfrac{d(i)f(t)}{f(i)}\)
      \(p_j\nmid i\Longrightarrow f(t)=2,d(t)=2d(i)\)
        d[1]=1;
        for (int i=2; i<=N; ++i) {
            v[i]||(p[++cnt]=i,f[i]=2,d[i]=2);
            for (int j=1,t; j<=cnt&&(t=p[j]*i)<=N; ++j) {
                v[t]=1;
                if (i%p[j]==0) { f[t]=f[i]+1,d[t]=d[i]/f[i]*f[t]; break; }
                f[t]=2,d[t]=(d[i]<<1);
            }
        }
    

    筛因数和 \(\sigma\)

    计算公式:设 \(n=\prod p_i^{k_i}\),则 \(\sigma(n)=\prod(1+p_i+p_i^2+\cdots+p_i^{k_i})\)
    \(p_1\) 为最小质因子,令 \(g(n)=1+p_1+p_1^2+\cdots+p_1^{k_1}\)

    • \(i\in\mathbb P\Longrightarrow g(i)=i+1,\sigma(i)=i+1\)
    • \(p_j\mid i\Longrightarrow g(t)=p_j\times g(i)+1,f(t)=\dfrac{f(i)g(t)}{g(i)}\)
      \(p_j\nmid i\Longrightarrow g(t)=p_j+1,\sigma(t)=\sigma(p_j)\times\sigma(i)\)
        s[1]=1;
        for (int i=2; i<=N; ++i) {
            v[i]||(p[++cnt]=i,g[i]=i+1,s[i]=i+1);
            for (int j=1,t; j<=cnt&&(t=p[j]*i)<=N; ++j) {
                v[t]=1;
                if (i%p[j]==0) { g[t]=p[j]*g[i]+1,s[t]=s[i]/g[i]*g[t]; break; }
                g[t]=p[j]+1,s[t]=s[p[j]]*s[i];
            }
        }
    

    筛一般的积性函数

    先鸽着,估计暂时也用不到。

    \(k\) 次幂

    具体地说,给定 \(n,k,P\),求 \(1^k\sim n^k\) 的值,对 \(P\) 取模。

    \(F(x)=x^k\),那么 \(F\) 是个积性函数,所以也可以线性筛。
    但是对于质数 \(i\),我们只能做到用快速幂 \(O(\log k)\) 求出 \(i^k\bmod p\)

    \(n\) 以内的质数个数 \(\pi(n)\approx\dfrac n{\ln n}\),因此筛法的时间复杂度为 \(O\left(n+\dfrac{n\log k}{\ln n}\right)\)


    洛谷P4449 于神之怒加强版

    给定 \(n,m,k\),计算

    \[\sum_{i=1}^n\sum_{j=1}^m\gcd(i,j)^k \]

    \(10^9+7\) 取模的结果。
    每个测试点包含 \(T\) 组数据,它们共用相同的 \(k\)

    \(1\le T\le 2\times10^3,1\le n,m,k\le 5\times10^6.\)

    推柿子的过程很常规,不写了,最后的结果是

    \[\sum_{T=1}^{\min(n,m)}\left(\sum_{d\mid T}d^k\mu(\tfrac Td)\right)\left\lfloor\dfrac nT\right\rfloor\left\lfloor\dfrac mT\right\rfloor \]

    关键在于括号里的部分怎么求。
    理论上说,它是 \(f(x)=x^k\)\(\mu\) 这两个积性函数的狄利克雷卷积,所以也是积性函数,应该可以用线性筛预处理。

    \(g(n)=\sum\limits_{d\mid n}d^k\mu(\tfrac nd)\)

    对于 \(i\in\mathbb P\),显然有 \(g(i)=i^k-1\)
    对于合数 \(t\),设 \(t=p\times q\),其中 \(p\)\(t\) 的最小质因子。
    \(p\nmid q\),根据积性即可得到 \(g(t)=g(p)\times g(q)\)
    \(p\mid q\),我们可以得出 \(g(t)=p^k\times g(q)\)。推导过程留作习题。

    至此可以用线性筛求 \(g\)。质数位置直接用快速幂计算。

    时间复杂度 \(O\left(n+\dfrac{n\log k}{\ln n}+T\sqrt n\right)\)

    #include<stdio.h>
    const int N=5000005,P=1e9+7;
    int T,n,m,k,cnt,t,ans,p[500005],s[N],g[N]; bool v[N];
    inline int min(int x,int y) { return x<y?x:y; }
    int power(int x,int y) {
        int s=1;
        while (y) (y&1)&&(s=1ll*s*x%P),x=1ll*x*x%P,y>>=1;
        return s;
    }
    
    int main() {
        scanf("%d%d",&T,&k),n=5000000,s[1]=g[1]=1;
        for (int i=2; i<=n; ++i) { // 筛子
            v[i]||(p[++cnt]=i,g[i]=power(i,k)-1);
            for (int j=1; j<=cnt&&(t=p[j]*i)<=n; ++j) {
                v[t]=1;
                if (i%p[j]==0) { g[t]=1ll*(g[p[j]]+1)*g[i]%P; break; }
                g[t]=1ll*g[p[j]]*g[i]%P;
            }
            (s[i]=s[i-1]+g[i])>=P&&(s[i]-=P); // 前缀和
        }
        while (T--) {
            scanf("%d%d",&n,&m),n>m&&(t=n,n=m,m=t);
            for (int l=1,r,t1,t2; l<=n; l=r+1) // 整除分块
                r=min(n/(t1=n/l),m/(t2=m/l)),
                ans=(1ll*t1*t2%P*(s[r]-s[l-1]+P)+ans)%P;
            printf("%d\n",ans),ans=0;
        }
        return 0;
    }
    
  • 相关阅读:
    xCode中怎样保存自己的代码块
    2015-03-13---抽象工厂(附代码),
    java nio 缓冲区(一)
    MFC获取各个窗体(体)之间的指针(对象)
    自己动手写神经网络,自己真的能够动手写神经网络嘛?
    Android招財进宝手势password的实现
    QQ三方登录
    UVA 10561
    Vagi单点登录1.0
    《反脆弱》:软件业现成的鲁棒性(Robust)换了个说法变成了作者的发明,按作者的理论推导出许多可笑愚蠢的原则来
  • 原文地址:https://www.cnblogs.com/REKonib/p/16230446.html
Copyright © 2020-2023  润新知