zoukankan      html  css  js  c++  java
  • hdu 3092 (简化的素数打表+dp+log的用法) ps(开数组和预处理时数组要大点处理多一点。。。)

    题意:给你一个3000以内的数s,让你把它分解成几个数的和,要求得到的这几个数最小公倍数最大

    对于两个数a,b,如果不互质那么对于答案的贡献最多是a*b,假设他们最大公约数为k,那么把a,b分为a,b/k,b-b/k三个数的积肯定会大于等于a*b;

    因为当b/k > 1 && b-b/k > 1时,

    (b/k-1)(b-b/k-1) >= 1。

    即b*(b-b/k) >= b。而当b/k==1时那就是a*b==a*(b-b/k)*k;

    所以相对肯定是两两互质对答案贡献大。

    然后如果一个数y = p^t1*q^t2*c。p和q均为素数,且(p, q) = (p, c) = (q, c) = 1。对答案贡献为y.

    那么我把y拆成p^t1和q^t2和c,这三个数的最小公倍数就是y。但是这三个数的和更小了,可以再加入一个数y-p^t-q^t-c,可能会使结果更大。

    然后也就是把所有小于s的素数预处理出来然后类似于背包,但是因为是要最大值才能模m,在过程中模不行

    所以取一个对数,按对数的大小即为实际的大小的方向取模

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <math.h>
    #include <algorithm>
    #include <queue>
    #include <map>
    using namespace std;
    const int N = 41000;
    const double en=2.718281828459;
    int s,m,pri[3105],ans[3010],num[3010],vis[3105];
    double dp[3005];
    int pow(int a,int b){
    int ans=1;
    for(int i=1;i<=b;i++)
        ans*=a;
    return ans;
    }
    void init()
    {
     int n=3005-1;
       int m=sqrt(3005-1+0.5);
        for(int i=2; i<=m; i++)
            if(pri[i]==0)
                for(int j = i*i; j <=n; j += i)
                    pri[j]=1;
        int cnt=0,i;
        for(i=2;i<=3003;i++)
            if(pri[i]==0){
              num[++cnt]=i;
            //cout<<i<<" "<<cnt<<endl;
            }
    }
    
    int main()
    {
       // freopen("in.txt","r",stdin);
        init();
        while(~scanf("%d%d",&s,&m))
        {
            if(s==1)
            {
                cout<<1<<endl;
                continue;
            }
            int i,j;
            for(i = 0; i <= s; i ++){
                dp[i]=0;
                ans[i]=1;
            }
    
           
          //cout<<"r"<<endl;
           for(j = 1; num[j] <=s; j ++)
                for(i=s;i>=num[j]; i--){
                    for(int k=1;pow(num[j],k)<=i;k++){
                        int v=pow(num[j],k);
                        if(dp[i]<dp[i-v]+log(v*1.0)){
                            dp[i]=dp[i-v]+log(v*1.0);
                            ans[i]=(ans[i-v]*v)%m;
                           
                       // cout<<ans[i]<<" "<<i<<" "<<num[j]<<endl;
                        }
    
                    }
                    //cout<<ans[i]<<" "<<i<<endl;
                    }
                cout<<ans[s]<<endl;
                }
    
    
        return 0;
    }

    log中底数的值:

    const double en=2.718281828459;

    log(en)==1;

    括号中为浮点数

    更高效的筛选素数模板

    (实质是利用了根号前后素数的位置)

     int n=3005-1;//n以内
       int m=sqrt(3005-1+0.5);
        for(int i=2; i<=m; i++)
            if(pri[i]==0)
                for(int j = i*i; j <=n; j += i)
                    pri[j]=1;
  • 相关阅读:
    搭建selenium+python自动化环境
    编写函数计算一个数字的长度
    编写函数digit(num, k),函数功能是:求整数num从右边开始的第k位数字的值,如果num位数不足k位则返回0。
    求m-n之间数字的和
    编写一个函数,生成4位数字的验证码
    编写一个函数,在页面上输出一个N行M列的表格,表格内容填充0~100的随机数字
    编写一个函数,计算三个数字的大小,按从小到大的顺序输出
    编写函数,判断一个字符串的内容是不是纯数字
    编写函数,求圆的面积
    编写一个函数,计算两个数字的和差积商
  • 原文地址:https://www.cnblogs.com/shimu/p/5725856.html
Copyright © 2011-2022 走看看