筛法
线性筛
感觉一直对线性筛理解的不是很到位,重新写一下原理
素数
首先筛素数有个初学
代码如下:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long int ll;
const int N=1e8+3;
int inline read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,q,tot;
int pri[N];
bool vis[N];
void xxs(){
for(int i=2;i<=n;i++){
if(!vis[i]){
pri[++tot]=i;
}
for(int j=1;j<=tot&&pri[j]*i<=n;j++){
vis[i*pri[j]]=1;
if(i%pri[j]==0) break;//这一句是关键,保证了只被最小质因子筛掉
}
}
}
int main()
{
n=read();q=read();
xxs();
for(int i=1;i<=q;i++){
int x;
x=read();
printf("%d\n",pri[x]);
}
return 0;
}
好了,然后我们发现数组里面是两个数的乘积,这也启发我们可以用线性筛出积性函数(比如欧拉函数和莫比乌斯函数),而因数和和因数个数恰好也都是积性函数(或许是?),所以可以线性筛出来
因数个数
首先先来说因数个数,先把任意一个数
先设两个数组:
来一波分类讨论:
以上三种情况就已经覆盖了所有情况,并且可以实现
核心代码如下:
void xxs(){
for (int i=2;i<=N;i++){
if(!vis[i]){
pri[++tot]=i;
num[i]=1;
sum[i]=2;
}
for (int j=1;j<=tot&&i*pri[j]<=N;++j)
{
vis[i*pri[j]]=1;
if (!(i%pri[j]))
{
num[i*pri[j]]=num[i]+1;
sum[i*pri[j]]=sum[i]/(num[i]+1)*(num[i*pri[j]]+1);
break;
}
sum[i*pri[j]]=sum[i]*2;
num[i*pri[j]]=1;
}
}
}
约数和
下面来说如何求约数和:首先我们发现每一个质因子有
根据上面的经验,我们发现线性筛过程中每次筛的时候都是用最小的质因子筛掉,这启发我们只需要开一个辅助数组来记录一下最小质因子所代表的等比数列的和就可以了(即
先开几个数组:
Upd:6/28,欧拉函数和莫比乌斯函数也来筛一筛
欧拉函数
欧拉函数的定义:
设
这两个式子需要结合欧拉函数的性质,在另一篇博客里会单独详细地介绍,在此只是写出筛法的式子,所以不再赘述
而我们可以根据这个递推式在 我只会
莫比乌斯函数
莫比乌斯函数定义:设
先把
void xxs(){
for(int i=2;i<=n;i++){
if(!vis[i]){
mi[i]=-1;//质数只有它本身一个质因子
pri[++tot]=i;
}
for(int j=1;j<=tot&&pri[j]*i<=n;j++){
vis[i*pri[j]]=1;
if(i%pri[j]==0){
//说明这个数有平方因子
mi[i*pri[j]]=0;
break;
}
mi[i*pri[j]]=-mi[i];//多了一个质因子
}
}
}
好吧不用讲原因了,直接看注释就好了
学欧拉函数性质和莫比乌斯反演去了,兄弟们再会