题意描述
给定一个正整数 n n n,将其分解成多个正整数之和的形式,求这几个正整数的最小公倍数最大值
输入格式
一个正整数 n n n
输出格式
一个整数 l c m m a x lcm_{max} lcmmax
样例
输入:7
输出:12
数据范围
对于20%的的数据
N
≤
50
N\leq 50
N≤50
对于40%的数据
N
≤
200
N\leq 200
N≤200
对于60%的数据
N
≤
300
N\leq 300
N≤300
对于100%的数据
N
≤
500
N\leq 500
N≤500
20pts:暴力!
首先我们可以显而易见的发现这道题可以暴力
强制把
n
n
n拆开,每次暴力计算
l
c
m
lcm
lcm
最后取
m
a
x
max
max即可
这种算法可以拿到20
p
t
s
pts
pts
但是大家试一试就可以发现这种暴力如果优化不错的话可以做到120左右
40pts:打表!
在20
p
t
s
pts
pts的做法上再优化一下,只要你有耐心,把100以上的数据都打一遍表
这样你可以拿到40
p
t
s
pts
pts
60pts:贪心+DP!
做到60
p
t
s
pts
pts的时候大家就要完全抛掉之前的各种玄学做法。
我们来考虑一种贪心策略
因为我们要求
l
c
m
lcm
lcm,所以我们要选出来的数尽量互质
怎么选呢?
我们可以每次选一个指数的
k
k
k次幂,直到凑完。
为什么这样是最优的呢?
- 即得易见平凡,仿照上例显然。
- 留作习题答案略,读者自证不难。
- 反之亦然同理,推论自然成立。
- 略去过程QED,由上可知证毕。
设
x
,
y
x,y
x,y为质数
如果我们选了一个
x
k
⋅
y
m
x^k\cdot y^m
xk⋅ym,那么很明显我们可以取
x
k
x^k
xk和
y
m
y^m
ym两个数
因为显然
x
k
+
y
m
≤
x
k
⋅
y
m
x^k+y^m\leq x^k\cdot y^m
xk+ym≤xk⋅ym
并且
x
k
x^k
xk和
y
m
y^m
ym都只受到一个质数的约束,但是
x
k
⋅
y
m
x^k\cdot y^m
xk⋅ym受到两个质数的约束,所以最后拆分成
x
k
+
y
m
x^k+y^m
xk+ym对最后的
l
c
m
lcm
lcm的贡献肯定
≥
\geq
≥
x
k
⋅
y
m
x^k\cdot y^m
xk⋅ym对答案的贡献
所以我们要把
n
n
n拆成积尽量大的几个质数的
k
k
k次方
这样答案就是乘积
以上是贪心内容
我们贪心之后我们就可以用计数类型的
d
p
dp
dp来解决这个问题
暴力拆分应该也能过
我们用
f
i
,
j
f_{i,j}
fi,j表示对于和为
i
i
i时,用前
j
j
j个质数可以凑出的最大
l
c
m
lcm
lcm
所以我们显然可以得到一个转移方程
f
i
,
j
=
m
a
x
{
f
i
−
p
r
i
m
e
j
k
,
j
−
1
×
p
r
i
m
e
j
k
}
f_{i,j}=max \{ f_{i-{prime_j}^k,j-1}\times {prime_j}^k \}
fi,j=max{fi−primejk,j−1×primejk}
答案就是 m a x { f i , j } max\{f_{i,j}\} max{fi,j},因为后面可以全是一堆1,所以要把每个凑成的 i i i都算一下。
以上是DP内容
不过还是有一些细节的,总之我调了快一个小时
把这个程序交上去发现只能拿到60 p t s pts pts,为什么呢?
100pts:高精!
这道题的答案是可能到1025次方左右的,所以即使开
l
o
n
g
l
o
n
g
long long
longlong也过不去
我们可以用一些毒瘤的__int128之类的东西
但是既然是备战noip
c
s
p
2019
csp2019
csp2019
还是打一个高精比较好吧
上代码(写完发现写的特别麻烦)
# 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;
}