0.积性函数
定义,若函数 ,则称 是积性函数。
若 ,则称 是完全积性函数。
1.埃拉托斯特尼筛法
我们发现,大于 的正整数 的 倍是合数,所以,我们可以从 开始,标记每个没有被标记的数的倍数,剩下的就是合数。
时间复杂度 。
可以用 bitset 优化,卡常后甚至可以 艹
bitset<MAXN> vis;vector<int> prime;inline void Sieve(){ vis[1] = 0; for(int i = 2;i <= n;i++) { if(!vis[i]) { prime.push_back(i); int t = i; while(t <= n) vis[t] = 1,t += i; } }}2.欧拉筛法
我们发现,埃筛会重复标记一些合数,所以我们只要不重复标记合数,时间复杂度就能做到 。
因此,我们可以在 时退出循环。因为这个时候 是 的倍数,被 筛过了,所以就不用计算多次。
代码如下。
#include <bits/stdc++.h>using namespace std;
constexpr int MAXN = 1e8 + 5;int n,q,k;bitset<MAXN> vis;vector<int> prime;signed main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); cin >> n >> q; vis[1] = 0; for(int i = 2;i <= n;i++) { if(!vis[i]) prime.push_back(i); for(int& j : prime) { if(i * j > n) break; vis[i * j] = 1; if(i % j == 0) break; } }}3.欧拉筛法筛积性函数
能 前提是能 计算 ,其中 是质数。如果不能 就会在后面跟一个 。
我们首先分解 ,所以 一定满足 ,否则就被 筛掉了。
若 ,则我们可以通过积性函数的定义直接得出 。
若 ,则上面就不成立了,需要记录 表示对于 的 , 一定大于 ,因此 ,带入定义就能算出来。
如果 ,说明 。
代码如下:
bitset<MAXN> vis;vector<int> prime;int low[MAXN],f[MAXN];inline void Sieve(){ vis[1] = low[1] = 1; for(int i = 2;i <= n;i++) { if(!vis[i]) { low[i] = i; prime.push_back(i); f[i] = ...; } for(int& j : prime) { if(i * j > n) break; vis[i * j] = 1; if(i % j == 0) { if(low[i] = i) f[i * j] = ...(一般由 f[i] 递推); else f[i * j] = f[i / low[i]] * f[low[i] * j]; break; } low[i * j] = j; f[i * j] = f[i] * f[j]; } }}