P1128 [HNOI2001] 求正整数 题解

前言

题目传送门

luogu博客食用更佳

本人最开始考虑的是贪心,结果WA声一片。

问题就出在没有找反例。。。。。。

后来用DP做对了。

还发现结合了高精度算法。

思路

比较难的高精度。

容易想到贪心,不过这个贪心的策略大多都能找到反例

我们可以考虑一下dp算法

首先,我们用 f [ i ] [ j ] f[i][j] f[i][j] 表示前 i i i 个质数,此时 n n n 的值为 j j j 的最小的答案。

利用高精度dp不太现实。就算上FFT也会T掉。

把它变成乘积的形式之后,我们可以将其变成 log ⁡ \log log 的形式就很容易转移了。

然后我们要干什么呢?

我们还得在做转移方程式的时候记录一下决策,最后做一遍高精。

值得一提的是 压位高精度时比如压 15 15 15 位那么最后输出的形式为:

printf("%015d",ans);

因为   m o d   1 e 15 \bmod 1e15 mod1e15 之后有效数位还有 15 15 15 个而并非 14 14 14 个.

Code

#include<bits/stdc++.h>
using namespace std;
int pri[18]={12,2,3,5,7,11,13,17,19,23,29,31,37};
int q[50005],top,c[18],an[18];
double hh[18],ans=1e6;
int n;
const int NN=1e7,N=7;
struct H{
	int a[3000],len;
}a;
void outit(H a){
    printf("%d",a.a[a.len]);
    for(int i=a.len-1;i;i--){
        for(int k=NN/10;a.a[i]<k;k/=10) cout<<0;
        if(a.a[i]) cout<<a.a[i];
    }
    cout<<endl;
}
H chen(H a,H b){
    H z;
    z.len=a.len+b.len+2;
    for(int i=1;i<=a.len;i++)
    for(int j=1;j<=b.len;j++)z.a[i+j-1]+=(a.a[i]*b.a[j]);
    for(int i=1;i<=z.len;i++)z.a[i+1]+=z.a[i]/NN,z.a[i]%=NN;    
    while(z.len>1&&!z.a[z.len])z.len--;
    return z;
}
void cheng(int x){
    for(int i=1;i<=a.len;i++)a.a[i]*=x;
    for(int i=1;i<=a.len;i++){
        a.a[i+1]+=a.a[i]/NN;
        a.a[i]%=NN;
    }
    while(a.a[a.len+1]){
        a.len++;a.a[a.len+1]+=a.a[a.len]/NN;a.a[a.len]%=NN;
    }
}
void mk(){
    for(int i=1;i<=pri[0];i++) hh[i]=log2(pri[i]);
    for(int i=n;i;i--) if(n%i==0) q[++top]=i;
}
void Gets(double x){
    ans=x;
    for(int i=1;i<=12;i++)an[i]=c[i];
}
void dfs(int x,int now,int t,double temp){
    if(ans<=temp)return;
    if(now==1){Gets(temp);return;}
    if(x>pri[0])return;
    for(int i=t;i<=top;i++)
        if(now%q[i]==0){
            c[x]=q[i]-1;
            dfs(x+1,now/q[i],i,temp+hh[x]*(q[i]-1));
            c[x]=0;
        }
}
int main()
{
    cin>>n;
    mk();
    dfs(1,n,1,0);
    a.a[1]=1;
    a.len=1;
    for(int i=1;i<=12;i++)
        for(int j=1;j<=an[i];j++)cheng(pri[i]);
    outit(a);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值