素数、约数、反素数
素数
判断素数
素数的判断:试除法
如果自然数n不能被[ 2 ,
n
\sqrt n
n]内所有素数整除,即为素数
(且素数有一个规律:大于4的素数一定在6的倍数的左右,即总是等于6x+1/6x+5)
代码:
bool prime(int n){
if(n<=3)return n>1;
if(n%6!=1&&n%6!=5)
return false;
for(int i=5;i*i<=n;i+=6)
if(n%i==0||n%(i+2)==0)return false;
return true;
}
查找一定范围内的所有素数(欧拉筛)
const ll N=1e6+10;
int prime[N],num;
bool vis[N];
void getprime(int n){
memset(vis,false,sizeof(vis));
num=0;
for(int i=2;i<=n;i++){
if(!vis[i])prime[num++]=i;
for(int j=0;j<num&&i*prime[j]<=n;j++){
vis[i*prime[j]]=true;
if(i%prime[j]==0)break;
}
}
}
练习:质数距离
题目
思路分析:
1.此题查找的范围大,如何缩小质数的查找范围呢?
若一个数x是合数,那么它一定能在[ 2 ,
x
\sqrt x
x]范围内找到至少一个素数能整除x:所以我们在查找素数的时候只需要查找到
r
\sqrt r
r就可以啦
2.查找后怎么将[l,r]范围内的素数找出来呢?
(由于我们没有把[l,r]范围内的所有素数找出来所以不能直接得到所有的素数)
可以先筛掉[l,r]范围内的合数。从每一个素数x,从max(x*2,(l+x-1)/x*x)
开始每次加素数(即素数的倍数)……标记合数,直到素数 * n>r的时候停止,每个素数重复此操作便可把[l,r]范围内所有的合数标记一遍剩下的即素数;
(x为素数)
max(x*2,(l+x-1)/x*x)
解释:
(l+x-1)/x*x
:是为了找到第一个在[l,r]范围内的合数,即
⌈
l
x
⌉
∗
x
\lceil \frac{l}{x} \rceil*x
⌈xl⌉∗x;
实际上为
⌈
l
x
⌉
\lceil \frac{l}{x} \rceil
⌈xl⌉,由于int为截断整数部分所以公式为如上形式;
x*2
:若
⌈
l
x
⌉
\lceil \frac{l}{x} \rceil
⌈xl⌉为1则应该为x*2
因为x本身为质数,
x
∗
⌈
l
x
⌉
=
x
∗
1
=
x
x* \lceil \frac{l}{x} \rceil= x * 1=x
x∗⌈xl⌉=x∗1=x 此时把x标记为合数就错了。
为了防止数据太大标记的时候超出数组的内存,我们可以做个小优化:
就是标记的是实际(合数
−
l
-l
−l)在后面查找素数的时候记得➕
l
l
l就可以啦
完成筛合数的工作就开始存放[l,r]范围内的质数然后遍历相邻质数两两之间的距离即可;
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10;
int prime[N],num;
bool vis[N];
void getprime(int n){
memset(vis,false,sizeof(vis));
num=0;
for(int i=2;i<=n;i++){
if(!vis[i])prime[num++]=i;
for(int j=0;j<num&&i*prime[j]<=n;j++){
vis[i*prime[j]]=true;
if(i%prime[j]==0)break;
}
}
}
int main(){
int l,r;
while(~scanf("%d%d",&l,&r)){
getprime(sqrt(r));
memset(vis,false,sizeof(vis));
//[l,r]内的所有合数筛掉
for(int i=0;i<num;i++){
ll c=prime[i];
for(ll j=max(2*c,(l+c-1)/c*c);j<=r;j+=c){
vis[j-l]=true;
}
}
num=0;
//存[l,r]范围内的质数
for(int i=0;i<=r-l;i++){
if(!vis[i]&&i+l>1){
prime[num++]=i+l;
}
}
if(num<2){
cout<<"There are no adjacent primes."<<endl;
}else{
int maxx=0,minn=0;
for(int i=0;i+1<num;i++){
int d=prime[i+1]-prime[i];
if(prime[maxx+1]-prime[maxx]<d)maxx=i;
if(prime[minn+1]-prime[minn]>d)minn=i;
}
printf("%d,%d are closest, %d,%d are most distant.\n",prime[minn],prime[minn+1],prime[maxx],prime[maxx+1]);
}
}
return 0;
}
约数
概念
约数,又称因数。整数a除以整数b(b≠0) 除得的商正好是整数而没有余数,我们就说a能被b整除,或b能整除a。a称为b的倍数,b称为a的约数。(摘自百度百科)
考点
唯一分解定理
唯一分解定理:任意一个大于1的自然数
n
n
n都能被表示成有限个素数的乘积且表示方法是唯一的;整理可以将相同素数的合并;可以得到公式
n
=
P
1
a
1
∗
P
2
a
2
∗
…
∗
P
n
a
n
n=P_1^{a_1} * P_2^{a_2} * …* P_n^{a_n}
n=P1a1∗P2a2∗…∗Pnan
P
i
P_i
Pi均为素数且
(
P
1
<
P
2
<
…
…
<
P
n
)
( P_1 < P_2 < ……<P_n)
(P1<P2<……<Pn)
约数个数
n的正约数个数为: ( a 1 + 1 ) ∗ ( a 2 + 1 ) ∗ ( a 3 + 1 ) ∗ … ∗ ( a n + 1 ) = ∏ i = 1 n ( a i + 1 ) ({a_1}+1)*({a_2}+1)*({a_3}+1)*…*({a_n}+1)= \prod_{i=1}^{n}({a_i}+1) (a1+1)∗(a2+1)∗(a3+1)∗…∗(an+1)=∏i=1n(ai+1)
约数之和
n的正约数之和: ( 1 + P 1 + P 1 2 + … + P 1 a 1 ) ∗ ( 1 + P 2 + P 2 2 + … + P 2 a 2 ) ∗ … ∗ ( 1 + P m + P m 2 + … + P m a m ) (1+{P_1}+{P_1}^2+…+{P_1}^{a_1})*(1+{P_2}+{P_2}^2+…+{P_2}^{a_2})*…*(1+{P_m}+{P_m}^2+…+{P_m}^{a_m}) (1+P1+P12+…+P1a1)∗(1+P2+P22+…+P2a2)∗…∗(1+Pm+Pm2+…+Pmam) = = = ∏ i = 1 n \prod_{i=1}^{n} ∏i=1n ( ∑ j = 0 a i \sum_{j = 0} ^{a_i} ∑j=0ai P i j {P_i}^j Pij)
题目:Aladdin and the Flying Carpet
一些解释
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10;
int prime[N],num;
bool vis[N];
ll s,d;
void getprime(int n){
memset(vis,false,sizeof(vis));
num=0;
for(int i=2;i<=n;i++){
if(!vis[i])prime[num++]=i;
for(int j=0;j<num&&i*prime[j]<=n;j++){
vis[i*prime[j]]=true;
if(i%prime[j]==0)break;
}
}
}
ll find(){
ll ans=1,ss=s;
if(s/d<d)return 0;
for(int i=0;i<num&&prime[i]*prime[i]<=s;i++){
int c=0;
while(s%prime[i]==0){
c++;
s/=prime[i];
}
ans*=(c+1);
}
if(s>1)ans*=2;//此时还有一个因子可以在原因数情况下选择乘或不乘两种情况,所以乘上2
ans/=2;//这里很好若出现正方形的情况在除以2 的时候亦可以把正方形给消去
for(int i=1;i<d;i++)//暴力除去边长小于d的情况
if(ss%i==0)ans--;
return ans;
}
int main()
{
int t,num=0;
cin>>t;
getprime(N);
while(t--){
cin>>s>>d;
printf("Case %d: %lld\n",++num,find());
}
return 0;
}
反素数
定义
对于任何正整数x,其约数的个数记作g(x)。例如g(1)=1、g(6)=4。
如果某个正整数x满足:g(x)>g(i) 且 0<i<x,则称x为反素数。例如,整数1,2,4,6等都是反素数。(摘自百度百科)
反素数可以理解为:约数相同时的最小数
结论
从中,我们可以得到:
若n为反素数,则一定满足:
n
=
2
t
1
∗
3
t
2
∗
5
t
3
∗
7
t
4
∗
…
n=2^{t_1}*3^{t_2}*5^{t_3}*7^{t_4}*…
n=2t1∗3t2∗5t3∗7t4∗… 且
t
1
≥
t
2
≥
t
3
≥
t
4
…
{t_1}\geq{t_2}\geq{t_3}\geq{t_4}…
t1≥t2≥t3≥t4…
即尽可能让质因子数量多且数值小
题目
题目
分析:
反素数的不同质因子不会超过10个:
2
∗
3
∗
5
∗
7
∗
11
∗
13
∗
17
∗
19
∗
23
∗
29
∗
31
>
2
∗
1
0
9
2*3*5*7*11*13*17*19*23*29*31>2*10^9
2∗3∗5∗7∗11∗13∗17∗19∗23∗29∗31>2∗109
反素数所有质因数总和不会超过30:
2
31
>
2
∗
1
0
9
2^{31}>2*10^9
231>2∗109
可以采用dfs暴搜
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=30;
int prime[N]={2,3,5,7,11,13,17,19,23,29};
int n,ans,sum=0;
/*
sum:记录最大约数个数
pos:待搜索质因子序号
last:待搜索质因子指数最大值
mul:约数之积
s:每轮约数个数
*/
void dfs(int pos,int last,int mul,int s){
//更新反素数
if(s>sum||(s==sum&&mul<ans)){
sum=s;
ans=mul;
}
for(int i=1;i<=last;i++){
if((ll)mul*prime[pos]>n)break;
mul*=prime[pos];
dfs(pos+1,i,mul,s*(i+1));
}
}
int main()
{
cin>>n;
dfs(0,30,1,1);
cout<<ans<<endl;
return 0;
}