专栏文章

题解:P9863 [POI 2021/2022 R2] arm

P9863题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@minot1kb
此快照首次捕获于
2025/12/02 05:54
3 个月前
此快照最后确认于
2025/12/02 05:54
3 个月前
查看原文

P9863 [POI 2021/2022 R2] arm

题目大意

初始时你有 11 个物品,你需要将物品的数量按若干次以下步骤增加到大于 nn 个。
  • 选择 11:将物品数量存储进数据库中,耗费 aa 的时间。
  • 选择 22:使物品数量增加等同于数据库中的数量,耗费 bb 的时间。
初始时数据库为空,问最小操作次数。

观察

操作序列可以划分为多个阶段,每个阶段包含:
  • 一次存储操作(耗时 aa
  • 若干次增加操作(每次耗时 bb
假设进行 kk 阶段操作,第 ii 阶段使用了 11 次存储操作、 XiXi 次增加操作,则:
  • 由于第一阶段的操作前有一个物品,因此第一次操作后的物品数量为 1+X11+X1 ;
  • 以上一阶段操作后的物品数量为一份,以后的每第 ii>1 i (i>1) 次操作都会使得物品数量增加 XiXi 倍,即乘 1+Xi1+Xi
综上可得,物品增长模式为:
最终数量 =(1+x1)×(1+x2)×...×(1+xk) = (1 + x₁) × (1 + x₂) × ... × (1 + xₖ)

数学优化

因此,对于固定的阶段数 kk,我们不难想到以下条件:
  • 乘积 n+1≥ n+1
  • 总时间 =k×a+(xi)×b= k×a + (∑xᵢ)×b 最小
接下来就很明显了,考虑使用二分的方法完成,具体请见代码。

代码详解

CPP
#include<bits/stdc++.h>
#define imx __int128
using namespace std;
imx n,a,b,ans,l,r,now;
// 安全计算x的k次方,防止溢出
imx spw(imx x,imx k){
    imx p=1;
    for(imx i=1;i<=k;i++){
        if(p>n) return n+2;    // 已超过n,提前返回
        p*=x;
        if(p>n) return n+2;    // 再次检查是否溢出
    }
    return p;
}

// __int128输入输出函数
imx iread(){
    imx x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=x*10+(ch-'0');
        ch=getchar();
    }
    return x*f;
}
void iprint(imx x){
    if(x<0){
        putchar('-');
        x=-x;
    }
    if(x>9) iprint(x/10);
    putchar(x%10+'0');
    return;
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    n=iread(),a=iread(),b=iread();
    // 初始答案:单阶段策略(存储1次,增加n次)
    ans=a+n*b;
    // 枚举阶段数k(从2到60)
    // 60是上限因为2^60 ≈ 1.15e18 > 1e18
    for(imx k=2;k<=60;k++){
        // 二分查找:找到最大的base使得base^k ≤ n+1
        l=1,r=n+1,now=1;
        while(l<=r){
            imx mid=(l+r)/2;
            if(spw(mid,k)<=n+1)
                now=mid,l=mid+1;
            else r=mid-1;
        }
        // 尝试不同的因子组合:now和now+1
        for(imx i=0;i<=k;i++){
            imx p1=spw(now,k-i);   // now^(k-i)
            imx p2=spw(now+1,i);   // (now+1)^i
            // 检查乘积是否满足条件
            if(p1>n||p2>n){
                // 单个因子已超过n
                imx t=now*(k-i)+(now+1)*i;  // 因子和
                ans=min(ans,k*a+(t-k)*b);   // 总时间
                break;
            }
            else{
                // 检查乘积是否会溢出或满足条件
                if(p2>0&&p1>(n+1)/p2){
                    imx t=now*(k-i)+(now+1)*i;
                    ans=min(ans,k*a+(t-k)*b);
                    break;
                }
                else if(p1*p2>=n+1){
                    // 乘积满足条件
                    imx t=now*(k-i)+(now+1)*i;
                    ans=min(ans,k*a+(t-k)*b);
                    break;
                }
            }
        }
    }
    iprint(ans);
    return 0;
}

关键

  • 此题必须使用 __int128 类型,部分编译器可能无法编译运行(例如我的Dev-C++ 6.5),不过洛谷的评测机是OK的;
  • 如果要使用 __int128必须用快读快写(即函数iread、iprint) !!! (吃掉我好几发CE);
  • 安全计算kk的次方,注意溢出。

评论

0 条评论,欢迎与作者交流。

正在加载评论...