线性筛
0.积性函数
定义,若函数 ,则称 是积性函数。
若 ,则称 是完全积性函数。
1.埃拉托斯特尼筛法
我们发现,大于 的正整数 的 倍是合数,所以,我们可以从 开始,标记每个没有被标记的数的倍数,剩下的就是合数。
时间复杂度 。
可以用 bitset 优化,卡常后甚至可以 艹
CPP
12345678910111213141516
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.欧拉筛法
我们发现,埃筛会重复标记一些合数,所以我们只要不重复标记合数,时间复杂度就能做到 。
因此,我们可以在 时退出循环。因为这个时候 是 的倍数,被 筛过了,所以就不用计算多次。
代码如下。
CPP
1234567891011121314151617181920212223242526
#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.欧拉筛法筛积性函数
能 前提是能 计算 ,其中 是质数。如果不能 就会在后面跟一个 。
我们首先分解 ,所以 一定满足 ,否则就被 筛掉了。
若 ,则我们可以通过积性函数的定义直接得出 。
若 ,则上面就不成立了,需要记录 表示对于 的 , 一定大于 ,因此 ,带入定义就能算出来。
如果 ,说明 。
代码如下:
CPP
1234567891011121314151617181920212223242526272829303132
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];
}
}
}