【数论】素数

365bet官网注册 📅 2025-08-20 15:24:09 ✍️ admin 👁️ 5300 ❤️ 235
【数论】素数

目录

素数判定

试除法

分解质因数

试除法

质数筛

埃式筛法

线性筛法(欧拉筛)

约数

试除法求约数

约数个数

其它结论

质数定理

反素数

素数判定

试除法

时间复杂度:O(sqrt(n))

核心:从小到大枚举,优化:n能整除d,n一定能整除n/d

bool isPrime(int n){

if (n<2)

return false;

for (int i=2;i<=n/i;i++){

if(n%i==0)

return false;

}

return true;

}

提示:关于判断条件的写法,还可写作i*i<=n(但有溢出风险),i<=sqrt(n)(sqrt执行速度慢)

分解质因数

试除法

时间复杂度:最坏O(sqrt(n)) 最好O(logn)

核心:从小到大枚举。优化:由于n中最多只会包含一个大于sqrt(n)的质因子,只需要单独处理大于sqrt(n)的

void divide(int n){

for (int i=2;i<=n/i;i++){

// 当枚举到i时,n已经将2~i-1都除尽了,所以i一定没有2~i-1的因子,i一定是个质数

if (n%i==0){

while(n%i==0){

n/=i;

}

cout<

}

}

if(n>1)

cout<

}

质数筛

埃式筛法

时间复杂度:O(nloglogn)

核心:把区间内每一个数的所有倍数删掉,所有剩下的数都是质数

优化:由于合数的倍数会被质数的倍数所覆盖,所以我们只需要删除所有质数的倍数即可

int prime[N],cnt;

bool f[N];

void getPrime(int n){

for (int i=2;i<=n;i++){

if (!f[i]){

prime[cnt++]=i;

for (int j=i+i;j<=n;j+=i)

f[j]=true;

}

}

}

线性筛法(欧拉筛)

时间复杂度:O(n)

核心:每一个数只会被它的最小质因子筛掉

1.i % pj == 0 :pj是从小到大枚举的质数,第一次出现i % pj == 0一定说明 pj 是 i 的最 小质因子

2.i % pj != 0:pj一定小于i的所有质因子,pj也一定是pj*i的最小质因子

int prime[N],cnt;

bool f[N];

void getPrime(int n){

for (int i=2;i<=n;i++){

if (!f[i]) prime[cnt++]=i;

for (int j=0;i*prime[j]<=n;j++){

f[prime[j]*i]=true;

if (i%prime[j]==0) break;

}

}

}

约数

试除法求约数

时间复杂度:O(sqrt(n))

核心:直接枚举即可,注意要判断( i != n/i )

vector getDivisor(int n){

vectorres;

for(int i=1;i<=n/i;i++){

if(n%i==0){

res.push_back(i);

if (i!=n/i)

res.push_back(i);

}

}

return res;

}

约数个数

原理:对于任意一个数,我们对它进行质因数分解,它总能写成,而它的因数则可以通过改变质因数p1,p2,……,pn的指数来组合。比如我们将72进行质因数分解,得到,那么72的所有因数有统一的表达式。

那么约数的个数也就很好求了,就是x,y的排列组合(每个数能取0-ki)即

补充一些估计:

1~n中约有nlnn个约数、nlnn个素数

逆向思维,想1~n中有多少个倍数,1的倍数有n个,2的倍数有n/2个,3的倍数有n/3个……n的倍数有n/n个。总共的约数也就是,约为nlnn

1~n中约数最多有1536个

约数之和

公式:

很好理解,直接把结论展开,每个括号里任意取一个出来,组合起来,都是一个约数

反素数

素数是含有因子最少的,反素数就是含有因子最多的(因子个数相同时取最小的),所以反素数是相对于一个集合来说的。

标准定义:对于某个正整数x,如果任何小于x的正数的约数个数都小于x的约数个数,则称为是反素数。

反素数的问题大多都是直接的板子题。

常见的有:(1)给定因子个数,求反素数 . (2)求n以内的最大的反素数

解法:

上面我们已经介绍了约数个数定理。

我们可以再观察一下反素数的特点。

1.质因数p1,p2,……,pn应该是从2开始连续的。因为因数的个数只和质因数的指数相关,如果不是从2开始连续的(比如为,那么与它因数个数相同,而且更小,所对应的数才应该是反素数)

2.指数k1>=k2>=……>=kn,这个也很好理解,因为反素数要求约数最多的里面最小的,所以肯定要给小的数分配的幂指数更多一点。

发现了这两个规律后,我们的解法也就出现了,我们只需要去枚举底数和指数,将它们进行组合。简单计算一下,可以发现最差的情况,每一个幂次都是1,只需要15个质数相乘就能达到6e18。而指数,也同样考虑最差的情况,也就是2^64,最多达到64,所以时间复杂度是可以接受的。

模板

需要的参数:当前枚举的是第几个素数,上一个素数的指数,约数数量,当前的值

(1)给定因子个数,求反素数

#include

using namespace std;

using ll=long long;

ll n;

ll prime[16]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};

ll res;

void dfs(int k,int last,ll num,ll val){

if (num>n||k>=16) return ;//再乘一个一定会比n大

if (num==n&&val

res=val;

return ;

}

for (int i=1;i<=last;i++){//枚举prime[k]的指数

if (val*prime[k]>res) break;

val*=prime[k];

dfs(k+1,i,num*(i+1),val);

}

}

int main() {

ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);

cin>>n;

res=LLONG_MAX;

dfs(0,63,1,1);//预先算一下2^x达到n

cout<

}

(2)求n以内的最大反素数

对于1e18,要使用unsigned long long,以防溢出

#include

using namespace std;

using ll=unsigned long long;

ll n;

ll prime[16]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};

ll res,maxd;//最大反素数的值,约数数量

void dfs(int k,int last,ll num,ll val){

if (val>res||k>=16) return ;//再乘一个一定会比n大

if (num>maxd||(num==maxd&&val

res=val;

maxd=num;

}

for (int i=1;i<=last;i++){//枚举prime[k]的指数

if (val*prime[k]>n) break;

val*=prime[k];

dfs(k+1,i,num*(i+1),val);

}

}

int main() {

ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);

cin>>n;

dfs(0,63,1,1);//预先算一下2^x达到n

cout<

}

相关推荐

华为Mate9和荣耀V9哪个好 华为Mate9和荣耀V9对比评测 买哪个|对比
写小说的软件
beat365在线官网

写小说的软件

📅 07-16 👁️ 3280
搒募的意思
365bet官网注册

搒募的意思

📅 07-10 👁️ 3384