hdu3092
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3092
题目描述:将一个数拆分成几个数之和,问这几个数能得到的最大的最小公倍数模上给定的一个数是多少?
分析:首先这些数互质时,最小公倍数最大,考虑到素数都是互质的,猜想可能都被拆分成了素数,
证明:假设这些数被拆分成了m1+m2+m3+...+mk;除mk是合数外其余数都是素数,
mk可以写成一个素数和另外一个数的乘积,mk=a*b,所求的最大的最小公倍数就是m1*m2*m3*...*mk;
然而由于a+b<a*b;将mk分成a+b+(mk-(a+b)),得到的结果肯定比直接加上mk要大,
所以就证明了这些数都为素数。
证明 a+b<a*b;
1 < (a-1)(b-1);(a>1 && b>1)
1 < a*b-a-b +1;
a+b+1 < a*b+1 ;
注意到这些素数的乘积可能特别大,所以两边同时去ln,变成加法;
#include <iostream> #include <cstdio> #include <cstring > #include <cstdlib> #include <algorithm> #include <cmath> using namespace std; #define maxn 3100 int prime[maxn]; double d[maxn]; int ans[maxn]; int vis[maxn]; int n,MOD; void init() { memset(ans,0,sizeof(ans)); memset(d,0,sizeof(d)); } int pow(int a,int b) { int answer=1; for(int i=1;i<=b;i++) answer*=a; return answer; } void solve() { for(int j=0;j<=n;j++) { d[j]=0; ans[j]=1; } for(int i=2;prime[i]<=n;i++) { for(int j=n;j>=0;j--) { for(int k=1;pow(prime[i],k)<=j;k++) //对于第k个素数,有两种选择 { int s=pow(prime[i],k); if(j>=s) if(d[j-s]+log(s*1.0) > d[j] ) { d[j]=d[j-s]+log(s*1.0); ans[j]= ( (ans[j-s]*s) )%MOD; } } //printf("%d ",ans[j]); } // printf("\n"); } printf("%d\n",ans[n]); } int main() { //cout<<log(2.718281828459)<<endl; int N=maxn-1; int m=sqrt(N+0.5); memset(vis,0,sizeof(vis)); for(int i=2;i<=m;i++) if(!vis[i]) for(int j=i*i;j<=N;j+=i) vis[j]=1; int t=1; for(int i=1;i<=N;i++) if(vis[i]==0) prime[t++]=i; /* for(int i=1;i<10;i++) { printf("%d ",prime[i]); }*/ //cout<<endl; while(~scanf("%d%d",&n,&MOD)) { init(); solve(); } return 0; }