杜教筛

我来填坑啦!(摘录自pengym大佬博客)


前置技能:

各种积性函数
  • 我们平时所惯用的数论函数都是积性函数。
  • 积性函数的定义:如果已知一个函数为数论函数,且f(1)=1f(1)=1,并且满足以下条件,若对于任意的两个互质的正整数p,qp,q都满足f(pq)=f(p)f(q)f(p*q)=f(p)*f(q),那么称这个函数为积性函数。
  • 特殊的,如果对于任意的正整数p,qp,q,也满足以上式子,则称这个函数为完全积性函数。
  • 而本文的杜教筛,正是用来筛积性函数前缀和的一种神奇筛法!(当然比线性筛优秀啦)
常见积性函数
  • μ(n)(n)–莫比乌斯函数。一会儿从另一种角度介绍它,顺便把上一节留的坑填一下。
  • φ(n)\varphi(n)–欧拉函数。表示不大于n且与n互质的正整数个数。
  • d(n)d(n)–约数个数。表示n的约数的个数。
  • σ(n)\sigma(n)–约数和函数。即n的各个约数之和。表示为:σ(n)=dnd\sigma(n)=\sum_{d|n}d

(PS:接下来的是完全积性函数)

  • ϵ(n)\epsilon(n)–元函数。ϵ(n)=[n=1]\epsilon(n)=[n=1]
  • I(n)I(n)–恒等函数。即对于所有的n,都为1。
  • id(n)id(n)–单位函数。id(n)=nid(n)=n
狄利克雷卷积(*)

基本知识

  • 定义:两个数论函数ffgg的卷积为(fg)(n)=dnf(d)g(nd)(f*g)(n)=\sum_{d|n}f(d)*g(\frac{n}{d})。前面的括号代表将f卷g,后面的括号代表范围。
  • 很显然,狄利克雷卷积满足以下运算规律:
    • 交换律;
    • 结合律;
    • 分配律;
(PS:积性函数*积性函数仍然为积性函数!)
莫比乌斯函数μ
  • 在上一节,我们了解了一个性质:dnμ(d)=[n=1]\sum_{d|n}μ(d)=[n=1]

  • 我们将这个性质转化为卷积的形式,即:μI=ϵμ*I=\epsilon。这在狄利克雷卷积中是一个很常用的恒等式。有了它,我们就可以补一下上一次的坑了!

  • 证明莫比乌斯反演:

    • 已知:

      F(n)=dnf(d)F(n)=\sum_{d|n}f(d)

      用狄利克雷卷积表示,即:F=fIF=f*I

      我们知道II与μ函数卷起来有特殊性质:

      Fμ=fIμF*μ=f*I*μ

      通过结合律:

      Fμ=f(Iμ)=fϵ=fF*μ=f*(I*μ)=f*\epsilon=f

      代入后得:f(n)=dnμ(d)F(nd)f(n)=\sum_{d|n}μ(d)*F(\frac{n}{d})

      同理可以证明出另一种形式:f(n)=ndμ(dn)F(d)f(n)=\sum_{n|d}μ(\frac{d}{n})F(d)

欧拉函数 φ
  • φ。欧拉函数有一个性质,dnφ(d)=n\sum_{d|n}\varphi(d)=n

  • 与上述方法类似,我们将它转化为狄利克雷卷积的形式:φI=id\varphi*I=id

  • 这时候有一个大胆的想法,两边同时卷上μ,会有什么发现呢?

  • 来填第二个坑,欧拉函数与莫比乌斯函数的关系。

    φIμ=idμ\varphi*I*μ=id*μ

    φϵ=idμ\varphi*\epsilon=id*μ

    φ(n)=idu=dnμ(d)nd\varphi(n)=id*u=\sum_{d|n}μ(d)*\frac{n}{d}

    同时两边除以n,即:

    φ(n)n=dnμ(d)d\frac{\varphi(n)}{n}=\sum_{d|n}\frac{μ(d)}{d}

杜教筛

  • 我们现在需要计算:i=1nf(i)\sum_{i=1}^{n}f(i)f(i)f(i)为积性函数。

  • PS:接下来是杜教筛的套路式,latex打着太麻烦了,我就手写拍图好吧QAQ。

    杜教筛

经过分析,只要当你的h(i)h(i)的前缀和很好求,能在较短时间内求出,那么我们对后面的式子进行整除分块,求S(n)(n)的复杂度为O(n23)O(n^{\frac{2}{3}})

筛欧拉函数

杜教筛

筛莫比乌斯函数

杜教筛

luoguP4213杜教筛模板

Coding

#include<bits/stdc++.h>
#include<tr1/unordered_map>
#define ll long long
using namespace std;
const int N=5e6+10;
int t,n,cnt,p[N],v[N],mu[N],phi[N],sum1[N];
ll sum2[N];
tr1::unordered_map<int,int>w;
tr1::unordered_map<int,ll>w1;
void get(int maxn){
    phi[1]=mu[1]=1;
    for(int i=2;i<=maxn;++i){
        if(!v[i]){
            p[++cnt]=i;mu[i]=-1;phi[i]=i-1;
        }
        for(int j=1;j<=cnt;++j){
            if(i*p[j]>maxn) break;
            v[i*p[j]]=p[j];
            if(i%p[j]==0){
                phi[i*p[j]]=phi[i]*p[j];
                break;
            }
            else mu[i*p[j]]=-mu[i],phi[i*p[j]]=phi[i]*(p[j]-1);
        }
    }
    for(int i=1;i<=maxn;++i) sum1[i]=sum1[i-1]+mu[i],sum2[i]=sum2[i-1]+phi[i];
}
ll disphi(int N){
    if(N<=5000000) return sum2[N];
    if(w1[N]) return w1[N];
    ll ans=1LL*(1+N)*N/2;
    for(int l=2,r;l<=N;l=r+1){
        r=N/(N/l);
        ans-=1LL*disphi(N/l)*(r-l+1);
    }
    return w1[N]=ans;
}
int dismu(int N){
    if(N<=5000000) return sum1[N];
    if(w[N]) return w[N];
    int ans=1;
    for(int l=2,r;l>=0&&l<=N;l=r+1){
        r=N/(N/l);
        ans-=dismu(N/l)*(r-l+1);
    }
    return w[N]=ans;
}
int main(){
    scanf("%d",&t);
    get(5000000);
    while(t--){
        scanf("%d",&n);
        printf("%lld %d\n",disphi(n),dismu(n));
    }
    return 0;
}