1 条题解
-
0
题意简述
有 个敌人,第 个敌人生命值 ,位置 (严格递增)。
玩家选择一个整数位置 (可以与敌人重合),然后每次攻击:- 对距离 为 的敌人造成 伤害,其中 是攻击力。
问:最少攻击次数,使得存在某个 ,至少 个敌人被击败(生命 )。
如果不存在这样的 ,输出 。
核心思路
直接枚举攻击次数 然后判断是否可行。
因为 越大越容易满足,所以可以二分答案。
二分可行性判断
假设我们固定攻击次数为 (即二分中的 )。
. 判断一个敌人能否在 次攻击内被击败
设需要 每次攻击至少造成的伤害 为:
因为 次总伤害需要 。
如果 ,则不可能击败该敌人(因为最大单次伤害为 )。
否则,该敌人可被击败。. 可击败敌人对应的 范围
每次攻击对位置 的敌人伤害为 ,需要:
即:
因此 必须在区间:
$$[x_i - (m - \text{need}), \; x_i + (m - \text{need})] $$
. 转化为区间覆盖问题
每个可击败的敌人对应一个整数区间 ,其中:
$$L_i = x_i - (m - \text{need}), \quad R_i = x_i + (m - \text{need}) $$注意: 可以是任意整数,所以区间端点需取整,但这里 和 都是整数,所以 都是整数。
问题变为:
是否存在一个整数点 ,被至少 个这样的区间覆盖?
. 区间覆盖计数(扫描线)
用差分思想:
- 对每个区间 :
- 在 处
- 在 处 (表示离开区间)
然后按位置从小到大扫描,累加覆盖数,一旦 则可行。
. 二分边界
- 下界 (不可能,但作为起始)
- 上界 可以设一个足够大的数,比如 。
但标程中说明:
最大答案不会超过 ,因为攻击一次至少造成 点伤害。
所以如果 仍不可行,输出 。
. 复杂度
- 二分:
- 每次判断:(排序事件,用 或离散化)
- 总:,可过。
. 对应代码逻辑(C++ 版)
ll lo = 0, hi = 1e10; // 上界设大一点 while (hi - lo > 1) { ll mid = (lo + hi) / 2; map<ll, int> ev; for (int i = 0; i < n; i++) { ll need = (h[i] + mid - 1) / mid; // ceil(hi / mid) if (need > m) continue; ll left = x[i] - m + need; ll right = x[i] + m - need + 1; // 开区间右端点 ev[left]++; ev[right]--; } ll sum = 0; bool ok = false; for (auto& [pos, delta] : ev) { sum += delta; if (sum >= k) { ok = true; break; } } if (ok) hi = mid; else lo = mid; }
. 最终答案
如果 说明不可行,输出 ,否则输出 。
. 样例验证
以第一个样例为例:
二分 :
- 敌人 区间:
- 敌人:
- 敌人:
- 敌人:
- 敌人:
- 敌人:
扫描线:
- 位置 被敌人,, 覆盖,共个 ,可行。
所以 可行,继续二分更小值,发现 不可行,答案 。
. 注意点
- 区间右端点处理成开区间,避免重复计数边界。
- 是整数位置,所以 直接取整。
- 若 ,该敌人永远无法在 次内击败,直接跳过。
- 1
信息
- ID
- 6483
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 3
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者