题意:首先定义一个数有k-1个素数约数,那么该数被称为k维数(还要算上1,具体不懂看题目中的样例)。给定两个数n和k(和上面的k没什么关系!),求k维数的第n个数(n<10000)。而且这里题目隐晦的说了,k的数值是三维数,那么我们就可以根据唯一分解定理分析这个K三维数只有如下的三种形态。1、p、p^2(其中p为素数,且最大素数不超过100),
本题目是一个非常好的数学题,根据上面的三种情况主要有如下结论:
1.当k=1时,结果直接为1
2.当k=p时,结果为prime[n]^(k-1),这里的prime[n]为从2开始的第n个素数
3.当k=p^2时,结果为p^(k-1)和(p1*p2)^(p-1)之中的第n个数(这里p1,p2为任意素数)
当然知道结论是不够的,本题需要解决几个难点
1.大数,这里用java解决简单了许多
2.上述的情况三时,到底如何知道第n个数是什么呢?
3.不断的大数运算是否会超时???这里算阶乘的时候我们全部用log算!这样节约了很多时间空间!!!
为了解决第二个问题直观的想法就是暴力,但是绝对的超时,这个也是学到的最巧妙的一个思路。
首先我们在最最开始的时候需要得到一个至少长度为10000的素数表(情况二就会用到了),我们在考虑情况三时往往想p1,p2值是多少然后乘起来比较大小,那么换一种思路,假如我们知道一个数a,且知道该数的一个素数约数b,那么判断a/b是否为素数,如果是说明满足了条件。
我们再去考虑一下如何得到素数表,如果用线性筛素数 的方法,我们不仅可以得到素数表,还能得到对应每个数的最小素数约数!!!那么上面的思路是否就可以实现了? 之后我们就可以用归并的思想计算情况三的第n个值是多少了!
给大家一个线性筛素数的版子,为什么是O(n)的算法,就是因为每一次筛素数的时候,都是只用其最小素数约数筛选了一次!
const int N = 105000;
int prime[N/10],pd[N+5],pnum;
void gp()
{
for(int i=2;i<=N;i++)
{
if( 0==pd[i] ) prime[++pnum]=pd[i]=i;
for(int j=1;j<=pnum && i<=N/prime[j];j++)
{
pd[i*prime[j]]=prime[j];
if( i%prime[j]==0 ) break;
}
}
}
下面根据我们的分析得出代码即可
import java.util.*;
import java.math.*;
import java.io.*;
public class Main{
public static void main(String[] args){
final int N = 105050; //可以得到10028个素数表
int pnum=0; //记录素数的个数
int [] prime=new int [10050]; //记录素数数组
int [] pd = new int [N+5]; //素数筛法中存储每一个数最小素数约数
int cnt=0; //计算满足p1*p2(都是素数)表是计数器
int [] pl = new int [10050]; //满足p1*p2的表
int n,k;
for(int i=1;i<=N;i++){ //初始化
pd[i]=0;
}
for(int i=2;i<=N;i++){ //素数筛法
if(pd[i]==0) prime[++pnum]=pd[i]=i;
for(int j=1;j<=pnum&&i*prime[j]<=N;j++){
pd[i*prime[j]]=prime[j];
if(i%prime[j]==0) break;
}
}
for(int i=6;i<=41200;i++){ //满足p1*p2的表,共10033个
int x=i/pd[i];
if(pd[i]!=x&&x==pd[x]){
pl[++cnt]=i;
}
}
Scanner cin = new Scanner(System.in);
while(cin.hasNext()){
n=cin.nextInt();
k=cin.nextInt();
int p1=0,p2=0;
for(int i=1;i<=pnum;i++){
if(k%prime[i]==0){
p1=prime[i];
p2=k/prime[i];
}
}
if(k==1){ //情况一
System.out.println(1);
}
else if(p2==1){ //情况二
BigInteger temp=BigInteger.valueOf(prime[n]);
temp=temp.pow(k-1);
System.out.println(temp);
}
else if(p1==p2){ //情况三
int i=1,j=1; //i和下面的x代表p^(k-1)的情况,j和y代表(p1*p2)^(p-1)的情况
while(true){
double x=(p1*p1-1)*Math.log10(prime[i]),y=(p1-1)*Math.log10(pl[j]); //生成两个大数的log值
BigInteger sum1=BigInteger.valueOf(prime[i]).pow(p1*p1-1),sum2=BigInteger.valueOf(pl[j]).pow(p1-1);
if(x<=y){
i++;
if(i+j-2==n) {
System.out.println(sum1);
break;
}
}
else {
j++;
if(i+j-2==n){
System.out.println(sum2);
break;
}
}
}
}
}
}
}