1 条题解

  • 0
    @ 2026-10-31 17:57:24

    思路

    题意:有 nn 只史莱姆,生命值分别为 xix_i。每次使用卷轴会对所有史莱姆造成 yy 点伤害;生命值降到 0 的立即死亡,幸存的史莱姆在受伤后按 ⌊x/2⌋\lfloor x/2 \rfloor 与 ⌈x/2⌉\lceil x/2 \rceil 分裂成两只(本体消失,两只新的都会受后续伤害)。求消灭全部史莱姆所需的最少卷轴次数。

    反向定义“能力值”

    设 f(k)f(k) 为:使用 kk 次卷轴,能够彻底消灭的单只史莱姆的最大初始生命值。显然 f(0)=0f(0)=0。

    考虑一次卷轴对初始生命值 xx 的作用:

    • 若 x≤yx \le y,直接在第一次打击中死亡;
    • 否则受伤后变为 x−yx-y,并分裂成 ⌊(x−y)/2⌋\lfloor (x-y)/2 \rfloor 与 ⌈(x−y)/2⌉\lceil (x-y)/2 \rceil 两只,二者的和正是 x−yx-y。

    剩余 k−1k-1 次卷轴会同时作用于这两只(以及场上所有史莱姆),因此只要能消灭其中较大的那只,较小的那只也一定能被消灭。于是要求

    $$\left\lceil \frac{x-y}{2} \right\rceil \le f(k-1) \iff x - y \le 2 f(k-1) \iff x \le y + 2 f(k-1).$$

    所以

    f(k)=y+2 f(k−1),f(0)=0.f(k) = y + 2\,f(k-1),\qquad f(0)=0.

    按此递推可得通项 f(k)=(2k−1) yf(k) = (2^k - 1)\,y。

    直观理解:每多用一次卷轴,可处理的生命值上界就翻一倍再多加一份 yy,这正好对应“分裂成两只、每只再被卷轴打一次”的结构。

    为什么只看最大值

    卷轴是对全体史莱姆同时生效的,因此“kk 次清场”等价于“每只史莱姆都能在 kk 次内被消灭”,也就是

    max⁡ixi≤f(k).\max_i x_i \le f(k).

    又因为 f(k)f(k) 关于 kk 严格单调递增,答案就是最小的 kk 使 f(k)≥mxf(k) \ge mx。所以读入时只需统计最大值 mxmx,其余数值不影响答案。

    与代码的对应

    代码中 ori 就是 f(cnt)f(\text{cnt}):初值 ori = 0 是 f(0)f(0),每轮 ori = ori*2 + y 就是一次 f(k)←2f(k−1)+yf(k) \leftarrow 2f(k-1)+y,cnt 同步加一,循环条件 ori < mx 保证结束时 f(cnt)≥mxf(\text{cnt}) \ge mx,此时 cnt 即答案。

    复杂度与溢出

    每组数据时间 O(n+log⁡mx)O(n + \log mx)(读入 nn 个数 + 迭代至多 log⁡2(1018)≈60\log_2(10^{18})\approx 60 次),全部数据合计约为 O(∑n+Tlog⁡1018)O(\sum n + T\log 10^{18}),空间 O(1)O(1)。

    溢出方面:进入循环时必有 ori < mx ≤ 1e18,因此更新后 ori ≤ 2e18 + 1e18 = 3e18,小于 long long 上限 9.2×10189.2\times 10^{18};代码用 #define int long long 保证全程 64 位计算。

    代码

    #include<bits/stdc++.h>
    #define int long long
    #define dd double
    #define str string
    #define el cout << '\n'
    #define re return
    #define ff first
    #define ss second
    #define pll pair<int,int>
    #define pdd pair<dd,dd>
    #define all(x) x.begin(),x.end()
    #define pb push_back
    using namespace std;
    
    // 每组测试:读入史莱姆数据,计算最少卷轴使用次数
    void go(){
        int n,y;cin>>n>>y;
        int mx = 0;                 // 全场最大的初始生命值
        // 卷轴对全体史莱姆同时生效,答案只取决于最大初始生命值
        for(int i=0;i<n;i++){
            int x;cin>>x;
            mx = max(mx, x);
        }
    
        // f(k)=2*f(k-1)+y 表示 k 次卷轴能消灭的最大初始生命值,f(0)=0
        // ori 即 f(cnt),循环到 f(cnt) >= mx 时 cnt 就是最少次数
        int ori = 0;
        int cnt = 0;
        while (ori<mx)
        {
            ori = ori*2+y;
            cnt++;
        }
    
        cout<<cnt;el;               // cnt 即最少需要的卷轴次数
        
    }
    
    signed main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);cout.tie(nullptr);
        int _=1;
        cin >>_;                  // 读入测试组数 T
        while(_--){
            go();
        }
        return 0;
    }
    
    • 1

    信息

    ID
    38
    时间
    1000ms
    内存
    256MiB
    难度
    3
    标签
    递交数
    0
    已通过
    0
    上传者