【HDU - 3092】Least common multiple(数学+完全背包)

本文探讨了一道数学算法题目,目标是将一个数S拆分成若干个互质的素数之和,以求得这些数的最小公倍数(LCM)乘积的最大值,并最终给出模M的结果。通过筛法获取素数,采用完全背包算法,利用取对数的方式避免溢出,实现了高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Partychen like to do mathematical problems. One day, when he was doing on a least common multiple(LCM) problem, he suddenly thought of a very interesting question: if given a number of S, and we divided S into some numbers , then what is the largest LCM of these numbers? partychen thought this problems for a long time but with no result, so he turned to you for help! 
Since the answer can very big,you should give the answer modulo M. 

Input

There are many groups of test case.On each test case only two integers S( 0 < S <= 3000) and M( 2<=M<=10000) as mentioned above.

Output

Output the largest LCM modulo M of given S.

Sample Input

6 23

Sample Output

6


Hint: you can divied 6 as 1+2+3 and the LCM(1,2,3)=6 is the largest so we output 6%23=6.

题意:将一个数拆分成几个数的和,并求出这几个数最小公倍数的乘积的最大值模M后的值。

思路:

最小公倍数乘积最大,所以当这几个数都互质的,也就是都是素数的时候,他们的lcm最大。

所以可以先筛出素数,然后进行完全背包,不过乘积范围很大,所以要用一种特殊的方式

就是取log,将乘法变成加法,用这个数组来维护当前位置最大值,然后用另外一个数组,维护结果。

ac代码:

/*
要不要选看dp数组,
ans数组还是正常的乘法(结果值),dp数组是取Log后的值 (用来比较的值)
正常的转移方程应该是 dp[j]=max(dp[j],dp[j-prime[i]]*k*prime[i]);
但是这样有可能会爆longlong,所以取了一个log,把乘法变成了加法,
而且答案是要取余的值直接比较Max是不对的,
所以分了两个数组一个用来比较大小,一个 用来存储结果。
之后的转移方程就变成了,dp[j]=max(dp[j],dp[j-prime[i]]+k*prime[i]) 
*/ 
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<vector>
using namespace std;
typedef long long ll;
const int MAXN=10000;
int prime[MAXN];
void init()
{
	for(int i=2;i<=MAXN;i++)
	{
		if(!prime[i]) prime[++prime[0]]=i;
		for(int j=1;j<=prime[0]&&prime[j]<=MAXN/i;j++)
		{
			prime[prime[j]*i]=1;
			if(j%prime[j]==0) break;
		}
	}
}

ll ans[10000];
double dp[10000];
int main() 
{
	init();
	int s,m;
	while(~scanf("%d%d",&s,&m))
	{
		for(int i=0;i<=s;i++)
		{
			dp[i]=0;
			ans[i]=1;
		}
		for(int i=1;i<=prime[0];i++)//完全背包 
		{
			if(prime[i]>s)break;
			for(int j=s;j>=prime[i];j--)
			{
				double tmp=log(prime[i]*1.0);
				for(int p=prime[i],q=1;p<=j;p*=prime[i],q++)
				{
					if(dp[j-p]+q*tmp>dp[j])
					{
						dp[j]=dp[j-p]+q*tmp;
						ans[j]=(ans[j-p]*p)%m;
					}
				}
				
			}
		}
		printf("%lld\n",ans[s]);
	}
	return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值