狄利克雷卷积 && 杜教筛 入门 — P4213 【模板】杜教筛(Sum)

狄利克雷卷积

数论函数

数论函数是指一类函数,其定义域是正整数,值域是一个数集

积性函数都是数论函数

常见的数论函数有

  • id(n)=nid(n) = n 单位函数,完全积性函数
  • ϵ(n)=[n=1]\epsilon(n) = [n = 1] 元函数,完全积性函数
  • d(n)d(n) 约数个数,积性函数
  • σ(n)\sigma(n) 约数和函数,积性函数
  • μ(n)\mu(n) 莫比乌斯函数,积性函数
  • φ(n)\varphi(n) 欧拉函数,积性函数

狄利克雷卷积

定义两个数论函数的狄利克雷卷积为 *:

t=fgt = f * g


t(n)=inf(i)g(ni) t(n) = \sum_{i|n} f(i) g(\lfloor \frac{n}{i} \rfloor)

在狄利克雷卷积中

  • 单位元是 ϵ\epsilon
  • fg=ϵf * g = \epsilon 则我们称 ggff 的逆元
  • fg=gff * g = g * f
  • (fg)h=f(gh)(f * g) * h = f * (g * h)
  • (f+g)h=fh+gh(f + g) * h = f * h + g * h
  • (xf)g=x(fg)(xf) * g = x(f*g)

一些应用

莫比乌斯反演

我们先来证明一点莫比乌斯反演里面的东西

dnμ(d)=[n=1] \sum_{d|n} \mu(d) = [n = 1]

很明显,化成卷积的形式即为
μ1=ϵ \mu * 1 = \epsilon

然后我们来证明莫比乌斯反演

g(n)=dnf(d)  f(n)=dnμ(d)g(nd) g(n) = \sum_{d|n} f(d)\ \ f(n) = \sum_{d|n} \mu(d) g(\lfloor \frac{n}{d} \rfloor)
显然可以化为

g=f1 g = f * 1

f=μg=μf1=fϵ=f \begin{aligned} f & = \mu * g \\ & = \mu * f * 1 \\ & = f * \epsilon \\ & = f \end{aligned}

等式成立,展开后得到

f(n)=dnμ(n)g(nd) f(n) = \sum_{d|n} \mu(n) g(\lfloor \frac{n}{d} \rfloor)

得证

欧拉函数与莫比乌斯函数

已知

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

显然

φ1=idφ1μ=idμφϵ=idμφ=idμ \begin{aligned} \varphi * 1 & = id\\ \varphi * 1 * \mu & = id * \mu\\ \varphi * \epsilon & = id * \mu\\ \varphi &= id*\mu \end{aligned}

展开得

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

除于nn

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

(然后你可以用这个式子证明已知的公式)(证明证明证明)

杜教筛

杜教筛用于在小于线性的时间求一个积性函数的前缀和

即 求 i=1nf(i)\sum_{i=1}^n f(i)

ff 为积性函数

为了愉快得求出这个式子我们需要设h=fgh = f * g

hhgg 均为积性函数

SSff 前缀和

然后Let's do it!

i=1nh(i)=i=1ndig(d)f(di)=d=1ng(d)i=1ndf(i)=d=1ng(d)S(nd) \begin{aligned} \sum_{i=1}^n h(i) & = \sum_{i=1}^n \sum_{d|i} g(d) f(\frac{d}{i}) \\ & = \sum_{d=1}^n g(d) \sum_{i=1}^{\frac{n}{d}} f(i) \\ &= \sum_{d=1}^n g(d) S(\frac{n}{d}) \end{aligned}

让我们做一点微小的改变

显然

g(1)S(n)=i=1nh(i)d=2ng(d)S(nd) g(1)S(n) = \sum_{i=1}^n h(i) – \sum_{d=2}^n g(d) S(\frac{n}{d})

除法分块 + 递归处理

只要你h(i)h(i)得前缀和能在大约O(1)O(1)左右得时间求出来(预处理后O(1)O(1)也可以的)时间复杂度就是O(n23)O(n^{\frac{2}{3}})

为什么?反正我是不会QAQ

来吧,上模板

Luogu P4213 【模板】杜教筛(Sum)

题目

莫比乌斯函数

S(n)=i=1nμ(i) S(n) = \sum_{i=1}^n \mu(i)

众所周知μ1=ϵ\mu * 1 = \epsilon

ϵ\epsilon 的前缀和过于显然——11

没什么说的了

欧拉函数

S(n)=i=1nφ(i) S(n) = \sum_{i=1}^n \varphi(i)

众所周知φ1=id\varphi * 1 = id

idid 的前缀和…等差数列了解一下

没什么说的了

代码

#include <cstdio>

const int N = 2e6 + 10;

int T, n;
int sum_mu[N], sum_mu2[3400];
int mu[N], phi[N], pri[N], pcnt;
int has_phi[3400], has_mu[3400];
long long sum_phi[N], sum_phi2[3400];
bool vis[N];

void pre(){
    vis[1] = true; mu[1] = 1; phi[1] = 1;
    sum_mu[1] = sum_phi[1] = 1;
    for(int i = 2; i < N; i++){
        if(vis[i] == false){
            pri[ ++pcnt ] = i;
            mu[i] = -1;
            phi[i] = i - 1;
        }
        for(int j = 1; j <= pcnt; j++){
            int tmp = pri[j] * i;
            if(tmp >= N) 
                break;
            vis[tmp] = true;
            if(i % pri[j] == 0){
                mu[tmp] = 0;
                phi[tmp] = phi[i] * pri[j];
                break;
            }
            mu[tmp] = -mu[i];
            phi[tmp] = phi[i] * (pri[j] - 1);
        }
        sum_mu[i] = sum_mu[i - 1] + mu[i];
        sum_phi[i] = sum_phi[i - 1] + (long long)phi[i];
    }
}

long long ask_phi(int x){
    if(x < N) return sum_phi[x];
    int tmp = n / x;
    if(has_phi[tmp] == n)
        return sum_phi2[tmp];
    has_phi[tmp] = n;
    long long &ans = sum_phi2[tmp];
    ans = (1LL * (1LL + x) * x) / 2;
    for(int left = 2, rig; left <= x; left = rig + 1){
        rig = x / (x / left);
        ans -= (rig - left + 1) * ask_phi(x / left);
    }
    return ans;
}

int ask_mu(int x){
    if(x < N) return sum_mu[x];
    int tmp = n / x;
    if(has_mu[tmp] == n)
        return sum_mu2[tmp];
    has_mu[tmp] = n;
    int &ans = sum_mu2[tmp];
    ans = 1;
    for(int left = 2, rig; left <= x; left = rig + 1){
        rig = x / (x / left);
        ans -= (rig - left + 1) * ask_mu(x / left);
    }
    return ans;
}

int main(){
#ifdef woshiluo
    freopen("luogu.4213.in", "r", stdin);
    freopen("luogu.4213.out", "w" ,stdout);
#endif
    pre();
    scanf("%d", &T);
    while(T--){
        scanf("%d", &n);
        printf("%lld %d\n", ask_phi(n), ask_mu(n)); 
    }
    fclose(stdin);
    fclose(stdout);
}

End

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

message
account_circle
Please input name.
email
Please input email address.
links

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理