1 条题解
-
0
思路
题意:有 只史莱姆,生命值分别为 。每次使用卷轴会对所有史莱姆造成 点伤害;生命值降到 0 的立即死亡,幸存的史莱姆在受伤后按 与 分裂成两只(本体消失,两只新的都会受后续伤害)。求消灭全部史莱姆所需的最少卷轴次数。
反向定义“能力值”
设 为:使用 次卷轴,能够彻底消灭的单只史莱姆的最大初始生命值。显然 。
考虑一次卷轴对初始生命值 的作用:
- 若 ,直接在第一次打击中死亡;
- 否则受伤后变为 ,并分裂成 与 两只,二者的和正是 。
剩余 次卷轴会同时作用于这两只(以及场上所有史莱姆),因此只要能消灭其中较大的那只,较小的那只也一定能被消灭。于是要求
$$\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).$$所以
按此递推可得通项 。
直观理解:每多用一次卷轴,可处理的生命值上界就翻一倍再多加一份 ,这正好对应“分裂成两只、每只再被卷轴打一次”的结构。
为什么只看最大值
卷轴是对全体史莱姆同时生效的,因此“ 次清场”等价于“每只史莱姆都能在 次内被消灭”,也就是
又因为 关于 严格单调递增,答案就是最小的 使 。所以读入时只需统计最大值 ,其余数值不影响答案。
与代码的对应
代码中
ori就是 :初值ori = 0是 ,每轮ori = ori*2 + y就是一次 ,cnt同步加一,循环条件ori < mx保证结束时 ,此时cnt即答案。复杂度与溢出
每组数据时间 (读入 个数 + 迭代至多 次),全部数据合计约为 ,空间 。
溢出方面:进入循环时必有
ori < mx ≤ 1e18,因此更新后ori ≤ 2e18 + 1e18 = 3e18,小于long long上限 ;代码用#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
- 上传者