0%

Hello 2026——C. War Strategy(二分答案可以减小实现难度)

题目大意

背景

有 n 个基地排成一条线,第 k 个基地是主基地(home base)。初始时主基地有 1 个士兵。

规则

每天按顺序发生以下事件:

  1. 选择一个基地 i 和该基地中任意数量的士兵(可以是 0 或全部),命令这些士兵向左移动到基地 i-1 或向右移动到基地 i+1(所有士兵必须同方向移动,且不能移出边界)

  2. 然后,一个新士兵出现在主基地 k(这个新士兵不受当天命令影响)

目标

给定:

  • n: 基地总数

  • m: 天数

  • k: 主基地位置

如果一个基地至少有 1 个士兵,则称其为"加固基地"(fortified)。求第 m 天结束时最多能有多少个加固基地。

输入输出

输入: 多组测试数据,每组包含三个整数 n, m, k (1≤k≤n≤10⁵, 1≤m≤10⁹)

输出: 每组数据输出第 m 天结束时最多能加固的基地数量

限制

  • 1 ≤ t ≤ 10⁴ (测试用例数)

  • 1 ≤ k ≤ n ≤ 10⁵

  • 1 ≤ m ≤ 10⁹

  • 所有测试用例的 n 之和不超过 2×10⁵

https://generals.io/ 应该是从这个游戏获取的灵感。

思路讲解

其实就是贪心,不过我们一开始写的贪心代码太屎山了,过不去,我重构了一下,写成了一个二分答案的代码,一次就过了。

那么二分答案,我们已经知道了这个需要占据 occupiedoccupied 个位置了,还有什么变量是不确定的吗?当然是有的,就是左边要占据多少个位置(needlneedl),右边要占据多少个位置(needrneedr)。 不过为了方便期间,我们定义左边为空比较少的(remlreml),右边为空比较多的(remrremr),即 remr>remlremr>reml。使用以下式子计算出 needlneedlneedrneedr

reml×2<occupied:needlreml,needroccupiedneedlothers:needloccupied2,needroccupiedneedlreml\times2<occupied:needl\gets reml,needr\gets occupied-needl\\ \text{others}:needl\gets\frac{occupied}{2},needr\gets occupied-needl

那么,我们已经知道了 needlneedlneedrneedr,那我们知道,最好的走法就是先走 needrneedr,然后再走 needlneedl,由于 needrneedlneedr≥needl,我们可以保证走左边的时候出兵点有足够的兵。因此,总的消耗为:

consumeneedl+needr+(needr1)consume\gets needl+needr+(needr-1)

那么最后 consumeconsumemm 比较一下就好了,那么 check 函数写出来了,整个二分答案就没有难点了。

⚠️注意:我们对传入的参数 occupiedoccupied 减了 1,因为出兵点是自动占领的嘛。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
auto check=[&](ll occupied) {
if (occupied>N) return false;
occupied--;
ll reml=max(0ll,K-1),remr=max(0ll,N-K);
if (reml>remr) {
swap(reml,remr);
}
ll needl=0,needr=0;
if (reml*2<occupied) {
needl=reml;
needr=occupied-reml;
}else {
needl=occupied/2;
needr=occupied-needl;
}
ll consume=needl+needr*2-1;
if (consume>M) {
return false;
}
return true;
};

AC代码

AC
https://codeforces.com/contest/2183/submission/358159596

心路历程(WA,TLE,MLE……)