素数and约数and反素数

素数

判断素数

素数的判断:试除法
如果自然数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 xlx
实际上为 ⌈ 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 xxl=x1=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=P1a1P2a2Pnan
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=2t13t25t37t4 t 1 ≥ t 2 ≥ t 3 ≥ t 4 … {t_1}\geq{t_2}\geq{t_3}\geq{t_4}… t1t2t3t4
即尽可能让质因子数量多且数值小

题目

题目
在这里插入图片描述
分析:
反素数的不同质因子不会超过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 235711131719232931>2109
反素数所有质因数总和不会超过30:
2 31 > 2 ∗ 1 0 9 2^{31}>2*10^9 231>2109
可以采用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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值