题意描述
给定一个正整数 ,将其分解成多个正整数之和的形式,求这几个正整数的最小公倍数最大值
输入格式
一个正整数
输出格式
一个整数
样例
输入:7
输出:12
数据范围
对于20%的的数据
对于40%的数据
对于60%的数据
对于100%的数据
20pts:暴力!
首先我们可以显而易见的发现这道题可以暴力
强制把
拆开,每次暴力计算
最后取
即可
这种算法可以拿到20
但是大家试一试就可以发现这种暴力如果优化不错的话可以做到120左右
40pts:打表!
在20
的做法上再优化一下,只要你有耐心,把100以上的数据都打一遍表
这样你可以拿到40
60pts:贪心+DP!
做到60
的时候大家就要完全抛掉之前的各种玄学做法。
我们来考虑一种贪心策略
因为我们要求
,所以我们要选出来的数尽量互质
怎么选呢?
我们可以每次选一个指数的
次幂,直到凑完。
为什么这样是最优的呢?
- 即得易见平凡,仿照上例显然。
- 留作习题答案略,读者自证不难。
- 反之亦然同理,推论自然成立。
- 略去过程QED,由上可知证毕。
设
为质数
如果我们选了一个
,那么很明显我们可以取
和
两个数
因为显然
并且
和
都只受到一个质数的约束,但是
受到两个质数的约束,所以最后拆分成
对最后的
的贡献肯定
对答案的贡献
所以我们要把
拆成积尽量大的几个质数的
次方
这样答案就是乘积
以上是贪心内容
我们贪心之后我们就可以用计数类型的
来解决这个问题
暴力拆分应该也能过
我们用
表示对于和为
时,用前
个质数可以凑出的最大
所以我们显然可以得到一个转移方程
答案就是 ,因为后面可以全是一堆1,所以要把每个凑成的 都算一下。
以上是DP内容
不过还是有一些细节的,总之我调了快一个小时
把这个程序交上去发现只能拿到60 ,为什么呢?
100pts:高精!
这道题的答案是可能到1025次方左右的,所以即使开
也过不去
我们可以用一些毒瘤的__int128之类的东西
但是既然是备战noip
还是打一个高精比较好吧
上代码(写完发现写的特别麻烦)
# include <cstdio>
# include <algorithm>
# include <cstring>
# include <cmath>
# include <climits>
# include <iostream>
# include <string>
# include <queue>
# include <vector>
# include <set>
# include <map>
# include <cstdlib>
# include <stack>
# include <ctime>
using namespace std;
# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define mct(a,b) memset(a,b,sizeof(a))
# define gc getchar()
typedef long long ll;
const int N=505;
const int inf=0x7fffffff;
const int mod=1e9+7;
template <typename T> void read(T &x){
x=0;int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
x*=f;
}
int n,a[N],tot,p[N];
int prime[N],k;
bool flag[N];
struct BigInt{
static const int M=50;
int num[M];
BigInt(){
memset(num,0,sizeof(num));
num[0]=1;
}
void write(){
_Rep(i,num[0],1)printf("%d",num[i]);
puts("");
}
BigInt operator * (const ll &x)const{
BigInt res;
res.num[0]=num[0];
Rep(i,1,res.num[0])res.num[i]+=num[i]*x;
Rep(i,1,res.num[0]){
res.num[i+1]+=res.num[i]/10;
res.num[i]%=10;
if(res.num[i+1])res.num[0]=max(res.num[0],i+1);
}
return res;
}
bool operator < (const BigInt &cmp)const{
if(num[0]!=cmp.num[0])return num[0]<cmp.num[0];
_Rep(i,num[0],1)if(num[i]!=cmp.num[i])return num[i]<cmp.num[i];
return true;
}
}f[N][N],ans;
ll gcd(ll a,ll b){
if(!b)return a;
return gcd(b,a%b);
}
ll lcm(ll a,ll b){
return a/gcd(a,b)*b;
}
int main()
{
freopen("lcm.in","r",stdin);
freopen("lcm.out","w",stdout);
read(n);
memset(flag,1,sizeof(flag));
Rep(i,2,n){
if(flag[i])prime[++k]=i;
for(int j=1;j<=k&&i*prime[j]<=n;j++){
flag[i*prime[j]]=false;
if(i%prime[j]==0)break;
}
}
Rep(i,0,n)f[i][0].num[0]=1,f[i][0].num[1]=1;
Rep(i,1,n)
Rep(j,1,k){
ll x=1;
while(x<=i){
f[i][j]=max(f[i][j],f[i-x][j-1]*x);
Rep(l,j+1,k)f[i][l]=max(f[i][l],f[i][j]);
x*=prime[j];
ans=max(ans,f[i][j]);
}
}
// f[4][1].write();
ans.write();
return 0;
}