HDU - 3092 Least common multiple (数论+背包)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3092点击打开链接

Least common multiple

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1036    Accepted Submission(s): 433


Problem Description
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

给出n,m

求正整数和为n所有情况中最大的lcm的值模m

对于n 只有小于他的质数和质数的幂对lcm有贡献 一开始不理解为什么是幂而不是倍数 因为如果是倍数说明还存在其他的因子 这使得在对物品划分的时候会产生影响

我们把n中所有的质数划分出来 进行01背包 

 注意这里是质数进行01背包 而不包含质数的幂 

原因在于相同质数为底的不同幂次间不互质(第一个样例中 如果2与4同时存在 他们的lcm!=2*4)

01背包中再加一层循环 枚举质数的幂次 这样就保证了不会出现上面的情况 

另外注意因为直接相乘的结果很大 同时不能在dp时取模 用对数是个很巧妙的办法 保证大小关系不变的同时 减小数据量

#include<bits/stdc++.h>
using namespace std;
long long int prime[3333];
void init()
{
    for(long long int i=2;i<3333;i++)
    {
        if(prime[i]==0)
        {
            for(long long int j=i*2;j<3333;j+=i)
            {
                prime[j]=1;
            }
        }
    }
}
vector<long long int >s;
double dp[3333];
long long int ans[3333];
int main()
{
    init();
    for(long long int i=2;i<3333;i++)
    {
        if(prime[i]==0)
        {
            s.push_back(i);
        }
    }
    long long int len=s.size();
    long long int n,m;
    while(scanf("%lld%lld",&n,&m)!=EOF)
    {
        memset(dp,0,sizeof(dp));
        for(int i=0;i<=n;i++)
            ans[i]=1;
        double mid=-1;
        long long int anss=1;
        for(long long int i=0;i<len;i++)
        {
            for(long long int j=n;j>=s[i];j--)
            {
                for(int k=s[i];k<=j;k*=s[i])
                if(dp[j]<dp[j-k]+log10(k))
                {
                    dp[j]=dp[j-k]+log10(k);
                    ans[j]=ans[j-k]*k%m;
                }
            }
        }
        for(int i=0;i<=n;i++)
        {
            if(mid<dp[i])
            {
                mid=dp[i];
                anss=ans[i];
            }
        }
        printf("%lld\n",anss);
    }
}





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值