模拟赛题目:lcm

题意描述

给定一个正整数 n n ,将其分解成多个正整数之和的形式,求这几个正整数的最小公倍数最大值

输入格式

一个正整数 n n

输出格式

一个整数 l c m m a x lcm_{max}

样例

输入:7
输出:12

数据范围

对于20%的的数据 N 50 N\leq 50
对于40%的数据 N 200 N\leq 200
对于60%的数据 N 300 N\leq 300
对于100%的数据 N 500 N\leq 500

20pts:暴力!

首先我们可以显而易见的发现这道题可以暴力
强制把 n n 拆开,每次暴力计算 l c m lcm
最后取 m a x max 即可
这种算法可以拿到20 p t s pts
但是大家试一试就可以发现这种暴力如果优化不错的话可以做到120左右

40pts:打表!

在20 p t s pts 的做法上再优化一下,只要你有耐心,把100以上的数据都打一遍表
这样你可以拿到40 p t s pts

60pts:贪心+DP!

做到60 p t s pts 的时候大家就要完全抛掉之前的各种玄学做法。
我们来考虑一种贪心策略
因为我们要求 l c m lcm ,所以我们要选出来的数尽量互质
怎么选呢?
我们可以每次选一个指数的 k k 次幂,直到凑完。
为什么这样是最优的呢?

  • 即得易见平凡,仿照上例显然。
  • 留作习题答案略,读者自证不难。
  • 反之亦然同理,推论自然成立。
  • 略去过程QED,由上可知证毕。

x , y x,y 为质数
如果我们选了一个 x k y m x^k\cdot y^m ,那么很明显我们可以取 x k x^k y m y^m 两个数
因为显然 x k + y m x k y m x^k+y^m\leq x^k\cdot y^m
并且 x k x^k y m y^m 都只受到一个质数的约束,但是 x k y m x^k\cdot y^m 受到两个质数的约束,所以最后拆分成 x k + y m x^k+y^m 对最后的 l c m lcm 的贡献肯定 \geq x k y m x^k\cdot y^m 对答案的贡献
所以我们要把 n n 拆成积尽量大的几个质数的 k k 次方
这样答案就是乘积

以上是贪心内容

我们贪心之后我们就可以用计数类型的 d p dp 来解决这个问题
暴力拆分应该也能过
我们用 f i , j f_{i,j} 表示对于和为 i i 时,用前 j j 个质数可以凑出的最大 l c m 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 \}

答案就是 m a x { f i , j } max\{f_{i,j}\} ,因为后面可以全是一堆1,所以要把每个凑成的 i i 都算一下。

以上是DP内容
不过还是有一些细节的,总之我调了快一个小时

把这个程序交上去发现只能拿到60 p t s pts ,为什么呢?

100pts:高精!

这道题的答案是可能到1025次方左右的,所以即使开 l o n g l o n g long long 也过不去
我们可以用一些毒瘤的__int128之类的东西
但是既然是备战noip c s p 2019 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;
}
发布了18 篇原创文章 · 获赞 3 · 访问量 1259

猜你喜欢

转载自blog.csdn.net/devout_/article/details/103043229
lcm