埃氏筛法
这个筛法是最朴素的筛法了,可以在
的时间内(基本
)筛出[1,n]中所有素数。实现非常简单,从2开始遍历,对于每个质数都暴力算出它的所有倍数并筛掉,根据欧拉的调和级数定理,这个时间是
级别的,但是只有质数才需要计算倍数,然后不知怎么回事复杂度就变成
了。
const int maxn = 100005, N = 100000;
int vis[maxn];
for(int i = 2; i <= N; i++) if(!vis[i])
for(int j = i + i; j <= N; j += i) vis[j] = 1;
最终vis数组中值为0的即为素数。
应用:给定n,m,求区间[n,m]中的质数个数。
。
首先我们会发现,如果区间中某个数不是质数,那么它必然有一个小于1E6的质因子。于是我们可以把[1,1E6]范围内的质数全部筛出来,然后用这些质数去筛区间[n,m]中的数字,复杂度应该是
。代码就不放了。
线性筛(欧拉筛法)
顾名思义,它可以在线性时间内筛出区间内质数及积性函数的值。但是在n小于1E6的时候甚至比埃氏筛法慢(因为常数大),于是它的主要功能就变成了求积性函数的值。
考虑如何做到线性。如果我们把每个数字都用它的最小质因子筛去的话就行了,但是怎么做呢?
从小到大枚举每个数字,再枚举当前已经筛出来的质数,筛掉数字乘质数的值。如果数字是当前质数的倍数就可以退出了,因为质数继续枚举就不是最小质因子了。因此此时效率为
。代码如下:
const int maxn = 100005, N = 100000;
int vis[maxn], prime[maxn], cnt;
for(int i = 2; i <= N; i++){
if(!vis[i]) prime[++cnt] = i;
for(int j = 1; j <= cnt; j++){
int p = prime[j], mul = p * i;
if(mul > N) break;
vis[mul] = 1;
if(i % p == 0) break;
}
}
上面说过了,这个东西还可以筛出积性函数的值。积性函数定义为对于任意互质的数对 。我们上面实际上算出了每个数的最小质因子,当然也可以算出积性函数的值了,当然我们需要计算任意的质数以及单个质数的幂次对应的f的值,因为这样才可以利用质因数分解计算出任意数字的f。
const int maxn = 100005, N = 100000;
int f[maxn], pw[maxn], vis[maxn], prime[maxn], cnt;
//pw[i]为i最小质因子的次数
for(int i = 2; i <= N; i++){
if(!vis[i]){
prime[++cnt] = i;
pw[i] = 1;
f[i] = ...;//要求给出i为质数时f(i)的值
}
for(int j = 1; j <= cnt; j++){
int p = prime[j], mul = p * i;
if(mul > N) break;
vis[mul] = 1;
if(i % p == 0){
f[mul] = f[i / pow(p, pw[i])] * f[pow(p, pw[i] + 1)];//要求给出i为质数的整数次幂时f的值
pw[mul] = pw[i] + 1;
break;
} else pw[mul] = pw[i] * pw[p];
}
}
当然了,具体计算的时候可以用一些方法把pow去掉。接下来就来列举一些积性函数的求值:
欧拉函数
小于等于n且与n互质的数的个数。定理:
因此我们会发现,若p为n的一个质因子,则有 .当p为质数时 。于是就可以愉快地写筛法了:
const int maxn = 100005, N = 100000;
int vis[maxn], phi[maxn], prime[maxn], cnt;
phi[1] = 1;
for(int i = 2; i <= N; i++){
if(!vis[i]){
prime[++cnt] = i;
phi[i] = i - 1;
}
for(int j = 1; j <= cnt; j++){
int p = prime[j], mul = p * i;
if(mul > N) break;
vis[mul] = 1;
if(i % p == 0){
phi[mul] = phi[i] * p;
break;
} else phi[mul] = phi[i] * phi[p];
}
}
于是我们就可以在 的时间内处理出[1,n]中所有数字的欧拉函数值了。
莫比乌斯函数
当n含有平方因子时函数值为0,n为质数时函数值为1,于是也可以线性筛了。
const int maxn = 100005, N = 100000;
int vis[maxn], mu[maxn], prime[maxn], cnt;
mu[1] = 1;
for(int i = 2; i <= N; i++){
if(!vis[i]){
prime[++cnt] = i;
mu[i] = -1;
}
for(int j = 1; j <= cnt; j++){
int p = prime[j], mul = p * i;
if(mul > N) break;
vis[mul] = 1;
if(i % p == 0) break;//mul含有平方因子,mu值为0
mu[mul] = -mu[i];//实际上是mu[mul] = mu[i] * mu[p];
}
}
于是我们的线性筛就可以用来做题了!
例题
求出上式的值,其中 。
根据欧拉函数的性质 ,可以化简上式得到
于是我们只需要计算出区间中欧拉函数的值就行了,复杂度 。当然这道题可以利用下面说的杜教筛进一步优化,把复杂度降到 。
杜教筛
杜教筛是求积性函数前缀和的一种方法。考虑如下的题目:
请求出如下表达式的值(
):
考虑使用欧拉函数的性质 ,得到 。带入得到:
考虑枚举 的值进行计算:
我们会发现后面那个向下取整的式子中只有 种不同的值,因此可以整除分块,带上记忆化搜索复杂度是 。但是我们可以用线性筛预处理出前 个欧拉函数的前缀和,杜教筛就可以取到最好的复杂度,为 。
同时我们会发现,杜教筛不仅求出了 ,也求出了任意的 。这个东西对于做题是非常有帮助的,比如上面最大公约数之和那题,我们就可以在外面的求和也整除分块一下,利用上面的值在较好的复杂度内完成算法。
例题:互质数对
求出所有的有序数对
。即如下算式:
考虑莫比乌斯函数的性质 ,原式转化为
于是我们的目标是求出莫比乌斯函数的前缀和。仿照上面的做法:
于是我们就顺利地在 的时间内求出了莫比乌斯函数前缀和,上面那题也解决了。
其实我们也可以直接利用欧拉函数的定义计算这道题,答案其实就是 。
例题:最小公倍数之和
求出如下表达式的值(
):
枚举最大公因数,可以得到( 下面默认除法为向下取整):
于是题目就可以化简为:
于是我们需要计算出g函数的前缀和即可。我们会发现如下性质: ,于是杜教筛:
于是这道题就可以在 的时间内解决了。
Min_25筛
在大部分情况下,杜教筛已经可以解决很多问题了,但是仍然有很多毒瘤题解决不了。这个时候就可以试一试Min_25筛(我也不造为啥叫这个名字),复杂度为
(我也不造为啥是这个复杂度)。
这个筛法常数比较小,内存也比较小,应该算一种挺不错的筛法了。我们以筛出[1,n]中的质数个数为例来说明这个筛法。
首先,他假设了[2,n]中所有数字全是质数,然后用数论办法模拟进行埃氏筛法。其实埃氏筛法中质数大小到
的级别时就没有数字可以被筛掉了。先预处理出这些质数,就可以开始Min_25筛了。我们令状态g(n,j)表示当前筛的区间是[1,n],已经筛完了前j个质数时剩下来的数字个数(即所有最小质因子大于第j个质数的数字和所有质数的总个数)。
我们考虑用g(n,j-1)推出g(n,j)。j-1时比j多了一些最小质因子为第j个质数的数字,要把他们减掉,由于g(n,j-1)中还存在比第j个质数小的质数,我们要把这些特殊情况去掉:
//id1和id2是离散化的数组,tot是小于sqrt(n)的质数个数
//prime是质数数组,num数组记n/i向下取整不同的值,从大到小排序,cnt为这些值的个数。
int sqr = (int)(sqrt(n) + 1);
for(int i = 1; i <= tot; i++){
int p = prime[i];
for(int j = 1; j <= cnt && p * p <= num[j]; j++){
int d = num[j] / p, k = d <= sqr ? id1[d] : id2[n / d];
g[j] -= g[k] - j + 1;
}
}
初始值就是把[2,n]中所有数字都当成质数的结果。当然,除了质数个数,我们还可以筛出来很多其它的东西。
设
在把所有数字都当成质数时是完全积性函数,即任意的
,我们可以用g数组筛出这些东西。只需要稍微修改一下上面的递推式即可:
考虑如何利用这个东西求出 积性函数 的前缀和。首先我们可以通过拆分等方法把所有质数的函数值之和算出来,然后定义S(n,j)为区间[2,n]中所有最小质因子大于等于第j个质数的数字函数之和,我们可以暴力枚举那些数字的最小质因子去计算,即得到
我也不造咋证的,这部分暴力计算的复杂度也是 ,于是我们就愉快的做完了。
例题
求如下表达式的值(
):
首先若n含有平方因子则答案为0,否则我们考虑i的质因子中是否跟n有相同的。首先我们用g筛出质数个数,然后来搞一搞S的递推式(其实如果枚举质数的次数超过1就不用算了,莫比乌斯函数值必然为0):
最后再加上1的情况,然后这道题就愉快地在 的时间内做完了……
推荐Min_25筛题目:51nod奇怪的数学题,loj简单的函数