标签:
题意:给你一个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;
hdu 3092 (简化的素数打表+dp+log的用法) ps(开数组和预处理时数组要大点处理多一点。。。)
标签:
原文地址:http://www.cnblogs.com/shimu/p/5725856.html