1. 程式人生 > >淺談積性函式的線性篩法

淺談積性函式的線性篩法

前置知識

線性篩

線性篩可以在嚴格$O(n)$的時間內篩出積性函式的值,

它有常見的套路

假設$n = p_1^{a_1} p_2^{a_2} \dots p_k^{a_k}$

如果我們能快速得出$f(p_i)$和$f(p_i^{k+1})$的取值,那麼直接套板子就行了

在下文中如無特殊說明,預設$p_i$表示$n$質因數分解之後第$i$個質數,$a_i$表示$p_i$的指數

常見的有以下幾種

線性篩素數

比較簡單,這也是篩其他積性函式的基礎

#include<cstdio>
const int MAXN = 1e4 + 10;
int N, prime[MAXN], vis[MAXN], tot;
void GetPrime(int N) { vis[1] = 1; for(int i = 2; i <= N; i++) { if(!vis[i]) prime[++tot] = i; for(int j = 1; j <= tot && i * prime[j] <= N; j++) { vis[i * prime[j]] = 1; if(!(i % prime[j])) break; } } } int main() { GetPrime(1e3);
for(int i = 1; i <= tot; i++) printf("%d ", prime[i]); return 0; }
線性篩素數

線性篩莫比烏斯函式

這個也是比較常見的

根據莫比烏斯函式的定義

$$\mu =\begin{cases}\left( -1\right) ^{k}\left( n=p_{1}p_{2}\ldots p_{k}\right) \\ 0\left( \exists P^{2}|n\right) \\ 1\left( n=1\right) \end{cases}$$

直接篩就可以了

#include<cstdio>
const
int MAXN = 1e4 + 10; int N, prime[MAXN], vis[MAXN], mu[MAXN], tot; void GetMu(int N) { vis[1] = mu[1] = 1; for(int i = 2; i <= N; i++) { if(!vis[i]) prime[++tot] = i, mu[i] = -1; for(int j = 1; j <= tot && i * prime[j] <= N; j++) { vis[i * prime[j]] = 1; if(!(i % prime[j])) { mu[i * prime[j]] = 0; break; } mu[i * prime[j]] = mu[i] * mu[prime[j]]; //根據莫比烏斯函式的定義,這裡也可以寫為 //mu[i * prime[j]] = -mu[i]; } } } int main() { GetMu(1e3); for(int i = 1; i <= tot; i++) printf("%d ", mu[i]); return 0; }
線性篩莫比烏斯函式

線性篩尤拉函式

這個貌似更常用一點qwq。

我在以前的文章中也詳細的講過,這裡只放一下程式碼

#include<cstdio>
const int MAXN = 1e4 + 10;
int N, prime[MAXN], vis[MAXN], phi[MAXN], tot;
void GetPhi(int N) {
    vis[1] = phi[1] = 1;
    for(int i = 2; i <= N; i++) {
        if(!vis[i]) prime[++tot] = i, phi[i] = i - 1;
        for(int j = 1; j <= tot && i * prime[j] <= N; j++) {
            vis[i * prime[j]] = 1;
            if(!(i % prime[j])) {
                phi[i * prime[j]] = phi[i] * prime[j];
                break;
            }
            phi[i * prime[j]] = phi[i] * phi[prime[j]];
        }
    }
}
int main() {
    GetPhi(1e3);
    for(int i = 1; i <= tot; i++)
        printf("%d ", phi[i]);
    return 0;
}
線性篩尤拉函式

線性篩約數個數

這個就稍微高階一點了,按照上面的套路,我們只需要考慮最小的質因子對每個數的貢獻

$n = p_1^{a_1} p_2^{a_2} \dots p_k^{a_k}$

設$d(i)$表示$i$的約數個數

那麼根據約數定理

$d(i) = \prod_{i = 1}^k (a_i + 1) $

記$a(i)$表示$n$的最小的質因子($a_1$)的指數。

$d(p_i) = 2$

當$i % p_j = 0$時,考慮$i * p_j$,實際上也就是$a_i$的指數多了$1$

我們先除去原來的,再加上新的就OK了

#include<cstdio>
const int MAXN = 1e4 + 10;
int N, prime[MAXN], vis[MAXN], D[MAXN], a[MAXN], tot;
void GetD(int N) {
    vis[1] = D[1] = a[1] = 1;
    for(int i = 2; i <= N; i++) {
        if(!vis[i]) prime[++tot] = i, D[i] = 2, a[i] = 1;
        for(int j = 1; j <= tot && i * prime[j] <= N; j++) {
            vis[i * prime[j]] = 1;
            if(!(i % prime[j])) {
                D[i * prime[j]] = D[i] / (a[i] + 1) * (a[i] + 2);
                a[i * prime[j]] = a[i] + 1;
                break;
            }
            D[i * prime[j]] = D[i] * D[prime[j]];
            a[i * prime[j]] = 1;
        }
    }
}
int main() {
    GetD(1e3);
    for(int i = 1; i <= tot; i++)
        printf("%d ", D[i]);
    return 0;
}
線性篩約數個數

線性篩約數和

同樣,按照上面的套路考慮

$n = p_1^{a_1} p_2^{a_2} \dots p_k^{a_k}$

設$SD(i)$表示$i$的約數和

$SD(n) = \prod_{i = 1}^k (\sum_{j = 1}^{a_i} p_i^j)$

$sum(i)$表示$i$最小的質因子的貢獻,即$sum(i) = \sum_{i = 1}^{a_1}p_1^j$

$low(i)$表示$i$最小質因子的指數,$low(i) = a_1$

有了這三個我們就可以轉移了

同樣是考慮$i$的最小的因子對答案的貢獻,應該比較好推,看程式碼吧

#include<cstdio>
const int MAXN = 1e4 + 10;
int N, prime[MAXN], vis[MAXN], SD[MAXN], sum[MAXN], low[MAXN], tot;
void GetSumD(int N) {
    vis[1] = SD[1] = low[1] = sum[1] = 1;
    for(int i = 2; i <= N; i++) {
        if(!vis[i]) prime[++tot] = i, sum[i] = SD[i] = i + 1, low[i] = i;
        for(int j = 1; j <= tot && i * prime[j] <= N; j++) {
            vis[i * prime[j]] = 1;
            if(!(i % prime[j])) {
                low[i * prime[j]] = low[i] * prime[j];
                sum[i * prime[j]] = sum[i] + low[i * prime[j]];
                SD[i * prime[j]] = SD[i] / sum[i] * sum[i * prime[j]];
                break;
            }
            low[i * prime[j]] = prime[j];
            sum[i * prime[j]] = prime[j] + 1;
            //這裡low和sum不是積性函式 
            SD[i * prime[j]] = SD[i] * SD[prime[j]];
        }
    }
}
int main() {
    GetSumD(1e3);
    for(int i = 1; i <= tot; i++)
        printf("%d ", SD[i]);
    return 0;
}
線性篩約數和

非常見積性函式的篩法

很多情況下我們會遇到求兩個積性函式狄利克雷卷積的情況

很顯然,這個函式也是積性函式,我們考慮如何求得

為了方便篩,我們需要把問題無限簡化,

設$low(i)$表示$p_1^{a_1}$

考慮篩法中最關鍵的地方

$i \% p_j = 0$,

有了$low(i)$,此時我們需要分兩種情況討論

1. $low(i) = i$,此時$i$一定是某個素數的冪的形式(否則就會break掉)

這裡就用到了我最開始說的那個套路

如果我們能快速的利用$f(p_i^{k})$更新出$f(p_i^{k + 1})$,那這個素數就很容易篩了

2. $low(i) \not = i$,那麼$i / low(i)$一定與$low(i) * p_j$是互質的,我們可以直接利用積性函式的性質去更新

C++版的虛擬碼

vis[1] = low[1] = 1; H[1] = 初始化 
for(int i = 2; i <= N; i++) {
    if(!vis[i]) prime[++tot] = i, mu[i] = -1, H[i] = 質數的情況, low[i] = i;
    for(int j = 1; j <= tot && i * prime[j] <= N; j++) {
        vis[i * prime[j]] = 1;
        if(!(i % prime[j])) {
            low[i * prime[j]] = (low[i] * prime[j]); 
            if(low[i] == i) H[i * prime[j]] = 特殊判斷;
            else H[i * prime[j]] = H[i / low[i]] * H[prime[j] * low[i]];
            break;
        } 
        H[i * prime[j]] = H[i] * H[prime[j]];
        low[i * prime[j]] = prime[j];
    }
}

參考資料

相關推薦

函式線性

前置知識 線性篩 線性篩可以在嚴格$O(n)$的時間內篩出積性函式的值, 它有常見的套路 假設$n = p_1^{a_1} p_2^{a_2} \dots p_k^{a_k}$ 如果我們能快速得出$f(p_i)$和$f(p_i^{k+1})$的取值,那麼直接套板子就行了 在下文中如無特殊說

線性,函式,狄利克雷卷,常見函式

一些性質 積性函式:對於函式\(f(n)\),若滿足對任意互質的數字\(a,b,a*b=n\)且\(f(n)=f(a)f(b)\),那麼稱函式f為積性函式。 狄利克雷卷積:對於函式f,g,定義它們的卷積為 \((f∗g)(n)=\sum_{d|n}f(d)g(\frac{n}{d})\)。 狄利克雷卷積滿足

[絕對能看懂!]Deciphering Password(函式+線性+素數分解)

描述 Xiaoming has just come up with a new way for encryption, by calculating the key from a publicly viewable number in the following way: Let t

函式求字首和

前置技能 積性函式的定義 若f(n)f(n)的定義域為正整數域,值域為複數,即f:Z+→Cf:Z+→C,則稱f(n)f(n)為數論函式。 若f(n)f(n)為數論函式,且f(1)=1f(1)=1,對於互質的正整數p,qp,q有f(p⋅q)=f(p)⋅f(q)f(p⋅

尤拉函式線性詳解

    該演算法在可線上性時間內篩素數的同時求出所有數的尤拉函式。     需要用到如下性質(p為質數):     1. phi(p)=p-1   因為質數p除了1以外的因數只

歐拉線性)與解函數

日常 rime ++ 下午 nbsp http image 發現 details 日常吐槽:嘖嘖嘖今天真是玄幻的一天。早上睡到9:10發現睡過了一個半小時,在9:30狂奔來機房 + 吃早餐,最後只剩一個半小時心態崩—>光榮爆零???又在下午四點把全部題改完

【數論】線性函式

尤拉函式: 定義:φ(n)表示1~n中和n互素的數目 性質: 首先可以根據概念得知,當n為素數時,顯然ϕ(n)=n−1 尤拉函式是個不完全積性函式,證明過程較複雜,貼個連結趕緊跑 根據它積性函式的性質和唯一分解定理,我們就可以把任意一個φ(n)

函式的性質及證明 + 線性

引言 在數論問題中,積性函式有著廣泛的應用。 如在莫比烏斯反演問題中,函式變換之後如何快速維護字首和往往是最重要也是最難的一步。如果維護的函式具有積性,那就可以嘗試利用線性篩在O(n)的時限內完成預處理,從而達到優化複雜度的神奇作用。 本文的大部分相關性質

杜教(整除分塊,函式,尤拉與莫比烏斯,狄利克雷卷

參考資料 整除分塊: 當我們求∑ni=1f([ni])∑i=1nf([ni])的時候,如果1到n求一遍感覺太傻了,因為會有很多重複的計算,例如:n=10000時,i在[101,111]時,都有[ni]=9[ni]=9,所以我們只需要對所有數分成如上的一個

bzoj 4407 於神之怒加強版 —— 反演+函式

題目:https://www.lydsy.com/JudgeOnline/problem.php?id=4407 推導如這裡:https://www.cnblogs.com/clrs97/p/5191506.html 然後發現 \( F(D) \) 是一個積性函式,可以篩質數的同時篩出來; 首先,單個質

BZOJ 3944 Sum (函式字首和杜教)

#include<bits/stdc++.h> using namespace std; #define debug puts("YES"); #define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++) #def

從SG函式解決博弈問題的通

                          從SG函式淺談解決博弈問題的通法   轉載:https://www.cnblogs.com

【總結】函式字首和(杜教

前言: 據CCH和LJH說,杜教篩似乎是一個非常套路的東西,幾乎所有的杜教篩的題目推理方式都是一模一樣的(但實測有些推理還是很噁心的)。所以複習杜教篩不需要太多時間,粗略看一遍,留下印象即可。 杜教篩其實是一種簡化運算的推理方式,它的使用條件並不僅限於積

NIO原理剖析與Netty初步----能服務器開發(一)

返回 創建 基於 register 訪問 io操作 nbsp info class 除特別註明外,本站所有文章均為原創,轉載請註明地址 在博主不長的工作經歷中,NIO用的並不多,由於使用原生的Java NIO編程的復雜性,大多數時候我們會選擇Netty,m

歐拉函數線性

style getc string nbsp rim log getchar 線性篩 tchar 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4

線性

模板 cnblogs clas int nbsp code 最小 oid log 模板 1 bool vis[MAXN]; 2 int prime[MAXN/10]; 3 4 void Prime(){ 5 int top=0; 6 for

【 數學基礎】【素數線性--歐拉模板】【普通的優化】

for ++ 自身 素數 spa prime pri 沒有 大於 質數(素數):指大於1的所有自然數中,除了1和自身,不能被其它自然數整除的數 合數:比1大,但不是素數的數稱為合數,合數除了被1和自身整除,還能被其它數整除 質因數(素因數或質因子):能整除給定正整數的質

hdu5528(函式+尤拉函式)

題意:設(題目已把f(6)的表給出),,給定n(n<=1e9),求g(n) 最主要的是求f(m),考慮到模數大於0的情況比較多,所以考慮考慮求ij mod n==0的情況。。 然後其實從題目給的表中看出對一個a來說,他0的個數和gcd(a,n)有關,其實也很容易證明,對一個數a來說

【hdu 5728 PowMod】【數論】【尤拉函式】【尤拉降冪遞迴取模】【尤拉函式

【連結】 http://acm.hdu.edu.cn/showproblem.php?pid=5728 【題意】 n是無平方因子的數 定義k=∑mi=1φ(i∗n) mod 1000000007,求K^k^k^k......%p 【思路】 先尤拉性質求出k

洛谷P2568 GCD(線性

題目連結:傳送門 題目: 題目描述 給定整數N,求1<=x,y<=N且Gcd(x,y)為素數的數對(x,y)有多少對. 輸入輸出格式 輸入格式: 一個整數N 輸出格式: 答案 輸入輸出樣例 輸入樣例#1: 複製 4 輸出樣例#1: 複製 4 說明 對於