杜教篩(概念及模板)

功能

杜教篩可以在非線性的時間內求出極性函數的前綴和。
洛谷給出的模板
對於n(n<231n<2^{31})求出:
ans1=i=1nφ(i)ans_1=\sum^{n}_{i=1}\varphi(i)
ans2=i=1nμ(i)ans_2=\sum^{n}_{i=1}\mu(i)
注:φ(i)\varphi(i)爲歐拉函數.μ(i)\mu(i)爲莫比烏斯函數,定義:
μ(x)={1x=1(1)kn,k0n1 \mu(x)=\left\{ \begin{aligned} 1&& 若x=1\\ (-1)^k&&若n沒有平方因數,k爲質因子個數\\ 0&&若n有大於1的平方因數 \end{aligned} \right.

所需要的知識

積性函數
引入幾個積性函數:
1.I(x)=12.ϵ(x)=[n==1]n=11,03.id(n)=n4.σ(n)=dnd5.d(n)=dn11.I(x)=1\\2.\epsilon(x)=[n==1]即n=1時爲1,其他時候爲0\\3.id(n)=n\\4.\sigma(n)=\sum_{d|n}d\\5.d(n)=\sum_{d|n}1
其中,前三種爲完全積性函數

狄利克雷卷積
設兩個數論函數f,gf,g則,它們的狄利克雷卷積是:
(fg)(n)=dnf(d)g(nd)(f*g)(n)=\sum_{d|n}f(d)g(\frac{n}{d})
狄利克雷卷積與其數論函數成羣,滿足結合律,交換律.同時具有封閉性,且擁有單位元及逆元
ϵ\epsilon函數爲狄利克雷卷積的單位元,這裏引入幾個性質:
1.μI=ϵ2.φI=id3.μid=φ1.\mu*I=\epsilon\\2.\varphi*I=id\\3.\mu*id=\varphi

莫比烏斯反演

g(n)=dnf(d)g(n)=\sum_{d|n}f(d)
則有
f(n)=dnμ(d)g(nd)f(n)=\sum_{d|n}\mu(d)g(\frac{n}{d})

證明:
不妨設
g=fIg=f*I

gμ=fIμg*\mu=f*I*\mu
由狄利克雷卷積性質1,即有
gμ=fϵ=fg*\mu=f*\epsilon=f
得證

杜教篩

現在,題目要求我們得出積性函數ff的前綴和,我們設:
S(n)=i=1nf(i)(1)S(n)=\sum_{i=1}^nf(i)\tag1
這時我們引入一個積性函數gg考慮fgf*g的前綴和,則有以下:
i=1n(fg)(i)=i=1ndif(d)g(id)=d=1ng(d)i=1ndf(i)=d=1ng(d)S(nd)(2)\begin{aligned} &\sum_{i=1}^n(f*g)(i)\\ &=\sum_{i=1}^n\sum_{d|i}f(d)g(\frac{i}{d})\\ &=\sum_{d=1}^ng(d)\sum_{i=1}^{\lfloor \frac{n}{d}\rfloor}f(i)\\ &=\sum_{d=1}^ng(d)S(\lfloor \frac{n}{d}\rfloor) \end{aligned}\tag2
第三行的推定:我們枚舉dd,對於每一個dd它的係數都是)i=1ndf(i))\sum_{i=1}^{\lfloor \frac{n}{d}\rfloor}f(i)
這時發現,如果我們當n==1n==1時的上式值,就便於求出SS
g(1)S(n)=d=1ng(d)S(nd)d=2ng(d)S(nd)(3)g(1)S(n)=\sum_{d=1}^ng(d)S(\lfloor \frac{n}{d}\rfloor)-\sum_{d=2}^ng(d)S(\lfloor \frac{n}{d}\rfloor)\tag3
根據(2)推定,便有杜教篩的核心公式
g(1)S(n)=i=1n(fg)(i)d=2ng(d)S(nd)(4)g(1)S(n)=\sum_{i=1}^n(f*g)(i)-\sum_{d=2}^ng(d)S(\lfloor \frac{n}{d}\rfloor)\tag4
如果我們可以找到合適的積性函數gg,使快速求出i=1n(fg)(i)\sum_{i=1}^n(f*g)(i)i=2ng(i)\sum_{i=2}^ng(i),我們就可以使用數論分塊處理.
時間複雜度
如果i=1n(fg)(i)\sum_{i=1}^n(f*g)(i)i=2ng(i)\sum_{i=2}^ng(i)的求解爲O(1)O(1)的,則杜教篩時間複雜度爲O(n23)O(n^{\frac{2}{3}})

總結

核心思想,就是設法構建杜教篩的核心公式(4),合理利用迪利克雷卷積的3個性質,以及常用積性函數,找到合適的ggi=1n(fg)(i)\sum_{i=1}^n(f*g)(i)i=2ng(i)\sum_{i=2}^ng(i)可以在很短的時間內求出,最後進行數論分塊.

模板

1.φ\varphi的前綴和
由迪利克雷卷積性質2,我們設
f=φ,g=If=\varphi,g=I
則,
fg=idf*g=id
顯然
fg=id\sum f*g=\sum id,
可以由通項公式n(n+1)2\frac{n*(n+1)}{2}進行O(1)O(1)求解
這裏給出代碼(轉自洛谷):

#define ll long long
ll phi[MAXN+10];
unordered_map<int,ll> ansphi;

ll S_phi(int n) {
    if (n<=MAXN) return phi[n];
    if (ansphi.find(n)!=ansphi.end()) return ansphi[n];
    ll ans=1ll*n*(n+1)/2;
    for (int l=2,r;l<=n;l=r+1)
        r=n/(n/l),ans-=(r-l+1)*S_phi(n/l);
    return ansphi[n]=ans;
}

這裏解釋:phi數組是預先處理好的歐拉函數的前綴和,(前MAXN項)ansphi是執行記憶化過程中得出的事先未能處理到的歐拉函數前綴和.函數中的for就是數論分塊求d=2ng(d)S(nd)\sum_{d=2}^ng(d)S(\lfloor \frac{n}{d}\rfloor)的過程,首先我們的g=Ig=I所以在這裏就是求d=2nS(nd)\sum_{d=2}^nS(\lfloor \frac{n}{d}\rfloor)
1.μ\mu的前綴和
由迪利克雷卷積性質1,設f=μ,g=I,f=\mu,g=I,fg=ϵf*g=\epsilon
fg=1\sum f*g=1
這裏給出代碼(轉自洛谷):

ll mu[MAXN];
unordered_map<int,ll> ansmu;
ll S_mu(int n) {
  if(n <= MAXN) return mu[n];
  if(ansmu.find(n) != ansmu.end()) return ansmu[n];
  ll ret = 1ll; 
  for(int l = 2, r; l <= n; l = r + 1) {
    r = n / (n / l); ret -= (r - l + 1) * S_mu(n / l);
  } return ansmu[n] = ret;
}

解釋mu,ansmu意義同上.
這裏給出μ\mu的打表方法(線性,類比歐拉函數的線性篩法.及性質):


bool check[MAX+10];  
int prime[MAX+10];  
int mu[MAX+10];  
void Moblus()  
{  
    memset(check,false,sizeof(check));  
    mu[1] = 1;  
    int tot = 0;  
    for(int i = 2; i <= MAX; i++)  
    {  
        if( !check[i] )
        {  
            prime[tot++] = i;  
            mu[i] = -1;
        }  
        for(int j = 0; j < tot; j++)  
        {  
            if(i * prime[j] > MAX) break;  
            check[i * prime[j]] = 1;  
            if( i % prime[j] == 0)
            {  
                mu[i * prime[j]] = 0;  
                break;  
            }
           else
                mu[i * prime[j]] = -mu[i];
        }  
    }  

下面是模板代碼:

#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#include <cstdio>
#include <cstdlib>
#define max(x, y) ((x)>(y)?(x):(y))
#define ll long long
using namespace std;
const int N = 2e6+5;
ll v[N], pri[N], phi[N], mu[N], cnt;
ll tot;
void init(int n)
{
    tot = n;
    mu[1] = phi[1] = 1;
    for (int i = 2; i <= n; i++)
    {
        if (v[i] == 0)
        {
            v[i] = i, pri[++cnt] = i;
            phi[i] = i - 1;
            mu[i] = -1;
        }
        for (int j = 1; j <= cnt; j++)
        {
            if (pri[j] > v[i] || pri[j] * i > n) break;
            v[i * pri[j]] = pri[j];
            phi[i * pri[j]] = phi[i] * (i % pri[j] ? pri[j]-1: pri[j]);
        }
        for (int j = 1; j <= cnt; j++)
        {
            if (i * pri[j] > n) break;
            if (i % pri[j] == 0){mu[i*pri[j]] = 0; break;}
            else mu[i*pri[j]] = -mu[i];
        }
    }
    for (int i = 2; i <= n; i++) mu[i] += mu[i-1], phi[i] += phi[i-1];
}
map<int, ll> ansphi, ansmu;
ll S_phi(int n)
{
    if (n < tot) return phi[n];
    if (ansphi.find(n) != ansphi.end()) return ansphi[n];
    ll ans = 1ll * n * (n + 1) / 2;
    for (int l = 2, r; l <= n; l = r + 1)
    {r=n/(n/l); ans -= (r - l + 1) * S_phi(n/l);}
    return ansphi[n] = ans;
}
ll S_mu(int n)
{
    if (n <tot) return mu[n];
    if (ansmu.find(n) != ansmu.end()) return ansmu[n];
    ll ans = 1ll;
    for (int l = 2, r; l <= n; l = r + 1)
    {r = n / (n / l); ans -= (r - l + 1) * S_mu(n / l);}
    return ansmu[n] = ans;
}
int main()
{
    int t;
    init(2e6);
    cin >> t;
    while(t--)
    {
        int n;
        scanf("%d", &n);
        printf("%lld %lld\n", S_phi(n), S_mu(n));
    }
}

一些杜教篩例子
φ(i)i\sum \varphi(i)*i,設f=φid,g=idf=\varphi*id,g=id
(fg)(n)=dnf(d)g(nd)=dn(φ(d)dnd)=ndnφ(d)=n2\begin{aligned} (f*g)(n)&=\sum_{d|n}f(d)g(\frac{n}{d})\\ &=\sum_{d|n}(\varphi(d)*d*\frac{n}{d})\\ &=n\sum_{d|n}\varphi(d)\\ &=n^2 \end{aligned}
fg=n(n+1)(2n+1)6\sum f*g=\frac{n(n+1)(2n+1)}{6}O(1)O(1)求出,gg也可以O(1)O(1)求出.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章