数论函数

定义域为整数,值域为复数的函数

积性函数:若 ,则

除数函数


除数函数为积性函数。

欧拉函数

表示不超过 且与 互质的正整数的个数

其中 的标准分解。
由此易见 函数是积性函数。
同时满足

线性求 函数:

#define int long long
int phi[3000005];
int n=3000000;
bool mark[3000005];
int prime[1000005];
int tot;
void getphi()
{
    phi[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(mark[i]==false)
        {
            prime[++tot]=i;
            phi[i]=i-1;
        }
        for(int j=1;j<=tot;j++)
        {
            if(i*prime[j]>n)
            {
                break;
            }
            mark[i*prime[j]]=true;
            if(i%prime[j]==0)
            {
                phi[i*prime[j]]=phi[i]*prime[j];
                break;
            }
            phi[i*prime[j]]=phi[i]*phi[prime[j]];
        }
    }
    for(int i=1;i<=n;i++)
    {
        phi[i]+=phi[i-1];
    }
}

函数

证明:
$$

其中

那么

根据二项式定理,易知该式子的值在 时值为 否则为 ,这也同时证明了

#define int long long
int mu[3000005];
int n=3000000;
bool mark[3000005];
int prime[1000005];
int tot;
void getmu()
{
    mu[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(mark[i]==false)
        {
            prime[++tot]=i;
            mu[i]=-1;
        }
        for(int j=1;j<=tot;j++)
        {
            if(i*prime[j]>n)
            {
                break;
            }
            mark[i*prime[j]]=true;
            if(i%prime[j]==0)
            {
                mu[i*prime[j]]=0;
                break;
            }
            mu[i*prime[j]]=-mu[i];
        }
    }
}

卷积



定义两个数论函数的卷积为:
$\text{Dirichlet}\varepsilon\varepsilon(n)=[n==1]\varepsilon1*\mu=\varepsilon\mu * \text{ID} = \varphi1*\text{ID}=\sigma$

莫比乌斯反演

公式:设 为两个数论函数。
如果有
$f=g*1g=f*\muf\mu=g1\mu= g\varepsilon=g1*\mu=\varepsilon$ )

同时,还有另一种反演:
如果有
$$

整除分块

当遇到形如
$O(\sqrt {n})i\frac{n}{i}\frac{n}{i}xx\frac{n}{x}$。

  • 证明:反证法:对于数,它所在的块的值为,且和数不在同一个块中。
    然后,原命题得证。

所以,易得计算原式方法。

for(int l=1,r;l<=n;l=r+1)
{
    r=n/(n/l);
    ans+=(r-l+1)*(n/l);
}

P2257 YY的GCD


由莫比乌斯反演:





改为枚举




代码:
//sum即为的前缀和

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int prime[10000005];
int mu[10000005];
ll f[10000005];
ll sum[10000005];
bool vis[10000005];
int cnt;
void init()
{
    mu[1]=1;
    for(int i=2;i<=10000000;i++)
    {
        if(vis[i]==false)
        {
            mu[i]=-1;
            prime[++cnt]=i;
        }
        for(int j=1;j<=cnt&&i*prime[j]<=10000000;j++)
        {
            vis[i*prime[j]]=true;
            if(i%prime[j]==0)
            {
                break;
            }
            mu[i*prime[j]]=-mu[i];
        }
    }
    for(int i=1;i<=cnt;i++)
    {
        for(int j=1;j*prime[i]<=10000000;j++)
        {
            f[j*prime[i]]+=mu[j];
        }
    }
    for(int i=1;i<=10000000;i++)
    {
        sum[i]=sum[i-1]+f[i];
    }
}
ll solve(int a,int b)//运用整除分块
{
    ll ans=0;
    if(a>b)
    {
        swap(a,b);
    }
    for(int l=1,r=0;l<=a;l=r+1)
    {
        r=min(a/(a/l),b/(b/l));
        ans+=(ll)(sum[r]-sum[l-1])*(a/l)*(b/l);
    }
    return ans;
}
signed main()
{
    init();
    int T;
    cin>>T;
    for(int i=1;i<=T;i++)
    {
        int a,b;
        scanf("%d %d",&a,&b);
        printf("%lld\n",solve(a,b));
    }
    return 0;
}

常用柿子


$$

调和数



杜教筛

快速求出
由于
可得


右边为子问题,可以递归解决,再加整除分块,可以保证每个点被计算一遍
复杂度

代码:

#include<bits/stdc++.h>
using namespace std;
#define re register
#define ll long long
#define N 5500000
ll phi[N+5];
int mu[N+5];
int prim[N+5];
bool mark[N+5];
int cnt;
unordered_map<int,ll> aphi;
unordered_map<int,int> amu;
void init()
{
    phi[1]=1;
    mu[1]=1;
    for(re int i=2;i<=N;i++)
    {
        if(mark[i]==false)
        {
            phi[i]=i-1;
            mu[i]=-1;
            prim[++cnt]=i;
        }
        for(re int j=1;j<=cnt;j++)
        {
            if(i*prim[j]>N)
            {
                break;
            }
            mark[i*prim[j]]=true;
            if(i%prim[j]==0)
            {
                phi[i*prim[j]]=phi[i]*prim[j];
                mu[i*prim[j]]=0;
                break;
            }
            phi[i*prim[j]]=phi[i]*phi[prim[j]];
            mu[i*prim[j]]=-mu[i];
        }
    }
    for(re int i=1;i<=N;i++)
    {
        phi[i]+=phi[i-1];
        mu[i]+=mu[i-1];
    }
}
ll getphi(int x)
{
    if(x<=N)
    {
        return phi[x];
    }
    if(aphi[x]!=0)
    {
        return aphi[x];
    }
    ll ans=(ll)x*(x+1)/2;
    for(int l=2,r=0;r!=2147483647&&l<=x;l=r+1)
    {
        r=x/(x/l);
        ans-=(ll)(r-l+1)*getphi(x/l);
    }
    return aphi[x]=ans;
}
int getmu(int x)
{
    if(x<=N)
    {
        return mu[x];
    }
    if(amu[x]!=0)
    {
        return amu[x];
    }
    int ans=1;
    for(int l=2,r=0;r!=2147483647&&l<=x;l=r+1)
    {
        r=x/(x/l);
        ans-=(r-l+1)*getmu(x/l);
    }
    return amu[x]=ans;
}
int main()
{
    init();
    int T;
    cin>>T;
    int x;
    for(int i=1;i<=T;i++)
    {
        scanf("%d",&x);
        printf("%lld %d\n",getphi(x),getmu(x));
    }
    return 0;
}