Skip to content

埃氏筛

基本思想是对于每个 2 的整数,把它们的非平凡倍数标记为不是质数。

埃氏筛法的流程大概是,我们从小到大考虑每个 ≥ 2 的整数,如果它没有标记则它是质数。

如果它是质数,我们把它的所有非平凡倍数标记为不是质数。

复杂度是 $n ·\sum \frac{1}{\text{prime}} = \mathcal{O}(n \log \log n) $

一个常数优化:对于一个质数 p,我们只用标记p2 的倍数。这并不改变复杂度。

欧拉筛

也被称为线性筛 (因为复杂度是线性)。

考虑从另一个方向优化标记,也就是对所有数考虑乘一个质数得到的新数。

由唯一分解定理,每个 2 的整数可以唯一被分解为形如 i=1kpi 的形式,其中 p1p2pkk1.

于是我们考虑标记的时候只在当前的 p 末尾加新的 pk+1,即我们保证枚举的质数 qpk

这样每个合数只会被枚举出来一次,因此复杂度是 O(n)

积性函数

定义

若函数 f(n) 满足 f(1)=1x,yN,gcd(x,y)=1 都有 f(xy)=f(x)f(y),则 f(n) 为积性函数。

若函数 f(n) 满足 f(1)=1x,yN 都有 f(xy)=f(x)f(y),则 f(n) 为完全积性函数。

性质

f(x)g(x) 均为积性函数,则以下函数也为积性函数:

h(x)=f(xp)h(x)=fp(x)h(x)=f(x)g(x)h(x)=dxf(d)g(xd)

x=piki

F(x) 为积性函数,则有 F(x)=F(piki)

F(x) 为完全积性函数,则有 F(x)=F(pi)ki

例子

  • 单位函数:ε(n)=[n=1]。(完全积性)
  • 恒等函数:idk(n)=nkid1(n) 通常简记作 id(n)。(完全积性)
  • 常数函数:1(n)=1。(完全积性)
  • 除数函数:σk(n)=dndkσ0(n) 通常简记作 d(n)τ(n)σ1(n) 通常简记作 σ(n)
  • 欧拉函数:φ(n)=i=1n[gcd(i,n)=1]
  • 莫比乌斯函数:μ(n)={1n=10d>1,d2n(1)ω(n)otherwise,其中 ω(n) 表示 n 的本质不同质因子个数,它是一个加性函数。

Dirichlet 卷积

定义

对于两个数论函数 f(x)g(x),则它们的狄利克雷卷积得到的结果 h(x) 定义为:

h(x)=dxf(d)g(xd)=ab=xf(a)g(b)

上式可以简记为:

h=fg

性质

交换律: fg=gf

结合律:(fg)h=f(gh)

分配律:(f+g)h=fh+gh

等式的性质: f=g 的充要条件是 fh=gh,其中数论函数 h(x) 要满足 h(1)0

证明: 充分性是显然的。

证明必要性,我们先假设存在 x,使得 f(x)g(y)。那么我们找到最小的 yN,满足 f(y)g(y),并设 r=fhgh=(fg)h

则有:

r(y)=dy(f(d)g(d))h(yd)=(f(y)g(y))h(1)0

fhghy 处的取值不一样,即有 fhgh。矛盾,所以必要性成立。

证毕

两个积性函数的 Dirichlet 卷积也是积性函数

证明: 设两个积性函数为 f(x)g(x),再记 h=fg

gcd(a,b)=1,则:

h(a)=d1af(d1)g(ad1),h(b)=d2bf(d2)g(bd2),

所以:

h(a)h(b)=d1af(d1)g(ad1)d2bf(d2)g(bd2)=dabf(d)g(abd)=h(ab)

所以结论成立。

证毕

例子

ε=μ1ε(n)=dnμ(d)d=11d(n)=dn1σ=id1σ(n)=dndφ=μidφ(n)=dndμ(nd)φ1=id

莫比乌斯反演

莫比乌斯反演是数论中的重要内容。对于一些函数 f(n),如果很难直接求出它的值,而容易求出其倍数和或约数和 g(n),那么可以通过莫比乌斯反演简化运算,求得 f(n) 的值。

莫比乌斯函数

μ 为莫比乌斯函数,定义为

μ(n)={1n=10n含有平方因子(1)kk 为 n 的本质不同质因子个数

性质

dnμ(d)={1n=10n1

dnμ(d)=ε(n)μ1=ε

证明

n=i=1kpici,n=i=1kpi

那么 dnμ(d)=dnμ(d)=i=0k(ki)(1)i=(1+(1))k

根据二项式定理,易知该式子的值在 k=0n=1 时值为 1 否则为 0,这也同时证明了 dnμ(d)=[n=1]=ε(n) 以及 μ1=ε

反演结论[gcd(i,j)=1]=dgcd(i,j)μ(d)

线性筛

由于 μ 函数为积性函数,因此可以线性筛莫比乌斯函数

cpp
void getMu() {
    mu[1] = 1;
    for (int i = 2; i <= n; ++i) {
        if (!flg[i])
            p[++tot] = i, mu[i] = -1;
        for (int j = 1; j <= tot && i * p[j] <= n; ++j) {
            flg[i * p[j]] = 1;
            if (i % p[j] == 0) {
                mu[i * p[j]] = 0;
                break;
            }
            mu[i * p[j]] = -mu[i];
        }
    }
}

莫比乌斯变换

f(n),g(n) 为两个数论函数。

形式一:如果有 f(n)=dng(d),那么有 g(n)=dnμ(d)f(nd)

这种形式下,数论函数 f(n) 称为数论函数 g(n) 的莫比乌斯变换,数论函数 g(n) 称为数论函数 f(n) 的莫比乌斯逆变换(反演)。

容易看出,数论函数 g(n) 的莫比乌斯变换,就是将数论函数 g(n) 与常数函数 1 进行狄利克雷卷积。

形式二:如果有 f(n)=n|dg(d),那么有 g(n)=n|dμ(dn)f(d)

例题

「SDOI2015」约数个数和

多组数据,求

i=1nj=1md(ij)(n,m,T5×104)

其中 d(n)=in1d(n) 表示 n 的约数个数


要推这道题首先要了解 d 函数的一个特殊性质

d(ij)=xiyj[gcd(x,y)=1]

再化一下这个式子

d(ij)=xiyj[gcd(x,y)=1]=xiyjpgcd(x,y)μ(p)=p=1min(i,j)xiyj[pgcd(x,y)]μ(p)=pi,pjμ(p)xiyj[pgcd(x,y)]=pi,pjμ(p)xipyjp1=pi,pjμ(p)d(ip)d(jp)

将上述式子代回原式

i=1nj=1md(ij)=i=1nj=1mpi,pjμ(p)d(ip)d(jp)=p=1min(n,m)i=1nj=1m[pi,pj]μ(p)d(ip)d(jp)=p=1min(n,m)i=1npj=1mpμ(p)d(i)d(j)=p=1min(n,m)μ(p)i=1npd(i)j=1mpd(j)=p=1min(n,m)μ(p)S(np)S(mp)(S(n)=i=1nd(i))

那么 O(n) 预处理 μ,d 的前缀和,O(n) 分块处理询问,总复杂度 O(n+Tn).

Code
cpp
// Date: 2023-07-30 13:22:17
// Problem: P3327 [SDOI2015] 约数个数和
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3327
// Memory Limit: 125 MB
// Time Limit: 1000 ms

......

constexpr int N = 50005;
int mu[N], pri[N], f[N], cnt;
bool st[N];

void init(){
    mu[1] = 1;
    F(i, 2, N) {
        if(!st[i]) pri[++ cnt] = i, mu[i] = -1;
        for(int j = 1; j <= cnt && i * pri[j] < N; ++j) {
            st[i * pri[j]] = 1;
            if(i % pri[j] == 0) {
                mu[i * pri[j]] = 0;
                break;
            }
            mu[i * pri[j]] = -mu[i];
        }
    }
    F(i, 1, N) mu[i] += mu[i - 1];
    F(i, 1, N) {
        for(int l = 1, r; l <= i; l = r + 1) {
            r = i / (i / l);
            f[i] += 1ll * (r - l + 1) * (i / l);
        }
    }
}

void solve(){
    int n, m;
    cin >> n >> m;
    if(n > m) swap(n, m);
    ll ans = 0;
    for(int l = 1, r; l <= n; l = r + 1){
        r = min(n / (n / l), m /(m / l));
                ans += 1ll * (mu[r] - mu[l - 1]) * f[n / l] * f[m / l];
        }
    cout << ans <<endl;
}

signed main(){
    fastio();
    int T;
    cin >> T;
    init();
    while(T--){
        solve();
    }
    return 0;
}