魔法信标网络
题意
座哨兵塔排成一排,第
座塔有
个信标。塔
的每个信标能覆盖所有满足
的塔
。塔
的"屏障强度"定义为所有能覆盖它的信标总数,即:
$$
现在可以额外部署 个信标到任意塔中,问:所有塔中最低屏障强度的最大值是多少?
思路
看到"最小值最大"这种措辞,你应该立刻想到什么?——二分答案。
为什么能二分?
如果最低屏障强度能达到 ,那么
肯定也能达到(少放几个信标就行)。这个单调性就是二分的基础。
二分之后怎么 check?
假设我们二分的目标是"所有塔的屏障强度都 ",怎么判断需要几个新信标?
先用前缀和 算出每座塔的初始屏障强度。然后从左到右扫描:
- 如果塔
的当前强度已经
,跳过。
- 如果塔
还差
才能达标,我们需要在某座塔上放
个信标来补。放在哪里最优?
关键观察:放在 最好。为什么?因为这个位置刚好还能覆盖到塔
(距离恰好
),而且向右延伸得最远,尽可能多地惠及后面的塔。
放完之后,这些新信标对后续塔的影响怎么维护?用差分数组。新增的 个信标放在位置
,它们覆盖
,但我们是从左到右扫的,所以只需记录它在右边什么时候"过期":在位置
处减去
。
每一轮 check 是 的。二分的范围?下界是初始最小强度,上界可以取所有信标之和加
(极端情况下
时每个信标覆盖全部塔)。二分轮数
。
总时间复杂度 ,其中
是答案的值域上界。
代码
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int radius, k, n;
cin >> radius >> k >> n;
vector<long long> beacons(n);
for(int i = 0; i < n; i++) cin >> beacons[i];
vector<long long> prefix(n+1, 0);
for(int i = 0; i < n; i++) prefix[i+1] = prefix[i] + beacons[i];
vector<long long> strength(n);
for(int j = 0; j < n; j++){
int lo = max(0, j - radius);
int hi = min(n-1, j + radius);
strength[j] = prefix[hi+1] - prefix[lo];
}
auto check = [&](long long target) -> bool {
long long used = 0;
vector<long long> diff(n+1, 0);
long long extra = 0;
for(int j = 0; j < n; j++){
extra += diff[j];
long long cur = strength[j] + extra;
if(cur < target){
long long need = target - cur;
used += need;
if(used > k) return false;
int p = min(j + radius, n - 1);
extra += need;
int end = min(p + radius, n - 1);
if(end + 1 <= n) diff[end + 1] -= need;
}
}
return true;
};
long long lo = *min_element(strength.begin(), strength.end());
long long hi = prefix[n] + k;
while(lo < hi){
long long mid = lo + (hi - lo + 1) / 2;
if(check(mid)) lo = mid;
else hi = mid - 1;
}
cout << lo << endl;
return 0;
}
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int radius = sc.nextInt();
long k = sc.nextLong();
int n = sc.nextInt();
long[] beacons = new long[n];
for (int i = 0; i < n; i++) beacons[i] = sc.nextLong();
long[] prefix = new long[n + 1];
for (int i = 0; i < n; i++) prefix[i + 1] = prefix[i] + beacons[i];
long[] strength = new long[n];
for (int j = 0; j < n; j++) {
int lo = Math.max(0, j - radius);
int hi = Math.min(n - 1, j + radius);
strength[j] = prefix[hi + 1] - prefix[lo];
}
long left = Long.MAX_VALUE;
for (long s : strength) left = Math.min(left, s);
long right = prefix[n] + k;
while (left < right) {
long mid = left + (right - left + 1) / 2;
if (check(strength, mid, k, radius, n)) left = mid;
else right = mid - 1;
}
System.out.println(left);
}
static boolean check(long[] strength, long target, long k, int radius, int n) {
long used = 0;
long[] diff = new long[n + 1];
long extra = 0;
for (int j = 0; j < n; j++) {
extra += diff[j];
long cur = strength[j] + extra;
if (cur < target) {
long need = target - cur;
used += need;
if (used > k) return false;
int p = Math.min(j + radius, n - 1);
extra += need;
int end = Math.min(p + radius, n - 1);
if (end + 1 <= n) diff[end + 1] -= need;
}
}
return true;
}
}
import sys
input = sys.stdin.readline
def solve():
radius = int(input())
k = int(input())
n = int(input())
beacons = list(map(int, input().split()))
prefix = [0] * (n + 1)
for i in range(n):
prefix[i + 1] = prefix[i] + beacons[i]
strength = [0] * n
for j in range(n):
lo = max(0, j - radius)
hi = min(n - 1, j + radius)
strength[j] = prefix[hi + 1] - prefix[lo]
def check(target):
used = 0
diff = [0] * (n + 1)
extra = 0
for j in range(n):
extra += diff[j]
cur = strength[j] + extra
if cur < target:
need = target - cur
used += need
if used > k:
return False
p = min(j + radius, n - 1)
extra += need
end = min(p + radius, n - 1)
if end + 1 <= n:
diff[end + 1] -= need
return True
left = min(strength)
right = prefix[n] + k
while left < right:
mid = left + (right - left + 1) // 2
if check(mid):
left = mid
else:
right = mid - 1
print(left)
solve()
const readline = require('readline');
const rl = readline.createInterface({ input: process.stdin });
const lines = [];
rl.on('line', line => lines.push(line.trim()));
rl.on('close', () => {
const radius = parseInt(lines[0]);
const k = parseInt(lines[1]);
const n = parseInt(lines[2]);
const beacons = lines[3].split(' ').map(Number);
const prefix = new Array(n + 1).fill(0);
for (let i = 0; i < n; i++) prefix[i + 1] = prefix[i] + beacons[i];
const strength = new Array(n);
for (let j = 0; j < n; j++) {
const lo = Math.max(0, j - radius);
const hi = Math.min(n - 1, j + radius);
strength[j] = prefix[hi + 1] - prefix[lo];
}
function check(target) {
let used = 0;
const diff = new Array(n + 1).fill(0);
let extra = 0;
for (let j = 0; j < n; j++) {
extra += diff[j];
const cur = strength[j] + extra;
if (cur < target) {
const need = target - cur;
used += need;
if (used > k) return false;
const p = Math.min(j + radius, n - 1);
extra += need;
const end = Math.min(p + radius, n - 1);
if (end + 1 <= n) diff[end + 1] -= need;
}
}
return true;
}
let left = Math.min(...strength);
let right = prefix[n] + k;
while (left < right) {
const mid = left + Math.ceil((right - left) / 2);
if (check(mid)) left = mid;
else right = mid - 1;
}
console.log(left);
});
复杂度
- 时间复杂度:
,其中
是二分的值域上界,
。
- 空间复杂度:
。

京公网安备 11010502036488号