HDU3092:Least common multiple(素数筛选+完全背包)

题意
给出\(n\)\(m\),将\(n\)拆成任意个数,求它们的最大的\(lcm\)

分析
1.可以证明\(n=p1^{s1}*p2^{s2}*...*pn^{sn}\)\(lcm\)最大(其中\(p1,p2...pn\)皆为素数) 证明
2.那么就可以转化为完全背包,模仿公式
\(f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<=v}\)
状态转移方程为\(dp[j]=max(dp[j],dp[j-p]*p)\);
用log代替真实值 \(dp[j]=max(dp[j],dp[j-p]+q*num)\);
来一个三重循环即可,复杂度小于\(O(n^2logn/log2)\)
3.用对数防止取模

代码

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <string>
#include <map>
#include <queue>
using namespace std;

#define ll long long
#define F(i,a,b) for(int i=a;i<=b;++i)
#define R(i,a,b) for(int i=a;i<b;++i)
#define mem(a,b) memset(a,b,sizeof(a))
#pragma comment(linker, "/STACK:102400000,102400000")
inline void read(int &x){x=0; char ch=getchar();while(ch<'0') ch=getchar();while(ch>='0'){x=x*10+ch-48; ch=getchar();}}

double dp[3030];
int prime[3030],p[3030],ans[3030],n,m,cnt;
void get_prime()//线性筛
{
    p[1]=1;
    F(i,2,3000)
    {
        if(!p[i]) prime[cnt++]=i;
        for(int j=0;j<cnt&&i*prime[j]<=3000;++j)
        {
            p[prime[j]*i]=1;
            if(i%prime[j]==0) break;
        }
    }
}
int main()
{
    get_prime();
    while(scanf("%d %d",&n,&m)==2)
    {
        F(i,0,n) { dp[i]=0;ans[i]=1; }
        for(int i=0;i<cnt&&prime[i]<=n;++i)
        {
            for(int j=prime[i];j<=n;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("%d\n",ans[n]);
    }
    return 0;
}
posted @ 2017-02-13 10:07  遗风忘语  阅读(196)  评论(0编辑  收藏  举报