hdu2447 K-dimension number By Assassin

题意:首先定义一个数有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;
                        }
                    }
                }
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值