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;
原文地址:https://www.cnblogs.com/shimu/p/5725856.html