(Luogu) P1463 [POI2002][HAOI2007]反素数 (打表 || 搜索)

传送门

解题思路:这题一看数据就不可能暴力的,但是我们可以先打个表试一下,发现题目所求的反质数分布非常的稀疏。所以解法一就是打表,打表有3种方法:

解法一:

一:直接暴力枚举因子(这种应该没什么人去做吧,太慢了)

二:枚举到sqrt(i),但是对于这么大的数据显然还是太慢了。

三:一个性质,约数个数定理:一个正整数n,可以分解成  (p1,p2...pn为素数)那这个数的约数个数为

(k1+1)*(k2+1)...(kn+1),这个不难理解,考虑p1^k1它的约数有p1^0~p1^k1共(k1+1)个,一直到pn^kn有(kn+1)个约数,根据乘法原理那n的约数个数就是(k1+1)*(k2+1)...(kn+1),因为每个单独的部分元素(如p1^2)都是n的约数,而且由于是素数的次方相乘起来唯一不重复,那搭配起来也是n的约数。

考虑到题意反质数就是约数相同的情况下数最小的那一个,那就是(k1+1)*(k2+1)...(kn+1)定,那我想要这个数小,那肯定是把大的k给小的p。所以我们可以得出k1,k2.....kn一定是不递增的。当k1,k2.....kn都取1时,前十个素数相乘已经大于2e9了,所以考虑前十个素数就行了。

先看打表的代码,只要表打出来了,那就非常简单明了了。

//打表
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const int maxn=2e9+5;
int prime[10]={2,3,5,7,11,13,17,19,23,29};
int main(){
	std::ios::sync_with_stdio(0);
	int count=0;
	for(int i=2;i<=maxn-5;++i){
		int tp=i,num=1;
		for(int j=0;j<10;++j){
			int cnt=0;
			while(tp%prime[j]==0){
				tp/=prime[j];
				cnt++;
			}
			num=num*(cnt+1);
			if(tp==1)	break;
		}
		if(num>count){
			cout<<i<<",";
			count=num;
		}
	}	
	return 0;
}



#include<bits/stdc++.h>
using namespace std;
int n;
int a[68] = {1, 2, 4, 6, 12, 24, 36, 48, 60, 120, 180,
 240, 360, 720, 840, 1260, 1680, 2520, 5040, 7560, 10080, 
 15120, 20160, 25200, 27720, 45360, 50400, 55440, 83160, 110880, 
 166320, 221760, 277200, 332640, 498960, 554400, 665280, 720720, 
 1081080, 1441440, 2162160, 2882880, 3603600, 4324320, 6486480, 
 7207200, 8648640, 10810800, 14414400, 17297280, 21621600, 32432400, 
 36756720, 43243200, 61261200, 73513440, 110270160, 122522400, 
 147026880, 183783600, 245044800, 294053760, 367567200, 551350800, 
 698377680, 735134400, 1102701600, 1396755360};
int main(){
	std::ios::sync_with_stdio(0);
	cin>>n;
	for(int i=67;i>=0;--i){
		if(a[i]<=n){
			cout<<a[i]<<endl;
			break;
		}
	}
	return 0;
}

解法二:

搜索也是用的约数定理,然后利用k1,k2.....kn一定是不递增的这一条件,枚举各个质数的指数,还是很好懂的,代码如下:

#include<bits/stdc++.h>
using namespace std;
const int inf=0x7f7f7f7f;
typedef long long ll;
ll prime[15]={0,2,3,5,7,11,13,17,19,23,29}; 
ll num[15];
ll ans,n,apnum;
//ans表示答案,apnum表示答案的约数个数
void dfs(ll step,ll pnum,ll state){//第几个质数,现在的约数个数,现在的值 
	if(step>=12)	return;
	if(pnum>apnum || (pnum==apnum&&state<ans)){
		ans=state,apnum=pnum;
	}
	num[step]=0; //每一轮都要从0开始 
	while(state*prime[step]<=n && num[step]+1<=num[step-1]){
		num[step]++;
		state*=prime[step];
		dfs(step+1,pnum*(num[step]+1),state);
	}
}
int main(){
	std::ios::sync_with_stdio(0);
	cin>>n;
	num[0]=inf;
	dfs(1,1,1); 
	cout<<ans<<endl;
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/TDD_Master/article/details/86524189