这是一个有亮点的题目~~
题目告诉K的因子个数d<=3
分析可知:(有唯一分解定理容易推出)
d=1时 K=1 ans=1
d=2时 K为质数 ans=pn^K-1
d=3时 K为质数p的平方 ans=pi^(p^2-1),或 ans=(pi*pj)^(p-1) , i不等于j (两个pi没啥关系)
前两种情况好处理。
第三种情况需要在形如pi^(p^2-1)和(pi*pj)^(p-1)这样的数中寻找第n小的数。
可以分别找出两者前n小的2n个数,再利用归并排序的思想找到两者中第n小的数。
对于pi^(p^2-1)这样的数,直接枚举质数就好。
然后,亮点来了~~
对于(pi*pj)^(p-1)这样的数
如果两重循环直接枚举pi和pj,为了确保可以得到前n个最小的乘积,需要先n*(n-1)/2的枚举,再排序,复杂度O(n^2+n^2log(n^2))。
n<10000,复杂度太高。
可以利用bfs+优先队列+set判重来枚举,效率还是挺高的,但是这题涉及大数,如果用JAVA来实现那些容器,不太好写。。。
可以换个思路,枚举乘积的结果,再判断这个结果可不可以分解成两个不同的质数。
在这里要介绍这个线性筛素数的代码,不仅可以O(n)得到1……n的素数表,还可以得到1……n中每个数的最小质因子,很强大的。
可以多读几遍,我的理解是每个合数X只唯一地被数X/p1筛掉,p1是X的最小素因子。
1 const int N = 105000; 2 3 int prime[N/10],pd[N+5],pnum; 4 5 void gp() 6 { 7 for(int i=2;i<=N;i++) 8 { 9 if( 0==pd[i] ) prime[++pnum]=pd[i]=i; 10 for(int j=1;j<=pnum && i<=N/prime[j];j++) 11 { 12 pd[i*prime[j]]=prime[j]; 13 if( i%prime[j]==0 ) break; 14 } 15 } 16 }
利用上面这段代码,可以很高效的实现刚才的思路。
对于枚举的乘积结果i,令x=i/pd[i], 则 若( pd[x]==x && x!=pd[i] )那么i就是合法的一个乘积结果啦。
枚举的界限可以定在prime[1]*prime[10001]=209486 。
问题解决啦~~
1 import java.util.*; 2 import java.math.*; 3 4 public class Main 5 { 6 public static void main(String[] args) 7 { 8 final int N = 105000; 9 int pnum=0; 10 int[] prime = new int[10050]; 11 int[] pd = new int[N+5]; 12 int cnt=0; 13 int[] p1 = new int[10005]; 14 15 for(int i=1;i<=N;i++) pd[i]=0; 16 for(int i=2;i<=N;i++) 17 { 18 if( 0==pd[i] ) prime[++pnum]=pd[i]=i; 19 for(int j=1;j<=pnum && prime[j]<=N/i;j++) 20 { 21 pd[i*prime[j]]=prime[j]; 22 if( i%prime[j]==0 ) break; 23 } 24 } 25 for(int i=6;i<=209486;i++) 26 { 27 int x=i/pd[i]; 28 if( pd[x]==x && x!=pd[i] ) 29 { 30 p1[++cnt]=i; 31 if( cnt>=10000 ) break; 32 } 33 } 34 35 Scanner scan = new Scanner(System.in); 36 int n,k; 37 while( scan.hasNextInt() ) 38 { 39 n=scan.nextInt(); 40 k=scan.nextInt(); 41 if( 1==k ) System.out.println("1"); 42 else if( pd[k]==k ) 43 { 44 BigInteger ret=BigInteger.valueOf(prime[n]); 45 ret=ret.pow(k-1); 46 System.out.println(ret); 47 } 48 else 49 { 50 int i=1,j=1,p=(int)Math.sqrt((double)k); 51 BigInteger ret; 52 while( true ) 53 { 54 double x=(double)(p-1)*Math.log10(p1[i]),y=(double)(k-1)*Math.log10(prime[j]); 55 if( x<y ) 56 { 57 i++; 58 if( i+j-2==n ) {ret=BigInteger.valueOf(p1[i-1]).pow(p-1);break;} 59 } 60 else if( x>y ) 61 { 62 j++; 63 if( i+j-2==n ) {ret=BigInteger.valueOf(prime[j-1]).pow(k-1);break;} 64 } 65 else 66 { 67 i++; 68 j++; 69 if( i+j-2>=n ) {ret=BigInteger.valueOf(p1[i-1]).pow(p-1);break;} 70 } 71 } 72 System.out.println(ret); 73 } 74 } 75 } 76 }