按到 1 号系统的距离平方排序,用后缀最大值扫描分界点,O(N log N) 求两套系统工作半径平方和的最小值。
OJ: roj
题目 ID: 3578
难度:普及+/提高-
标签:排序思维python
创建: 2026-10-02 08:37
更新: 2026-10-10 11:30
形式化题目
平面上有两套固定位置的拦截系统
每颗导弹
把导弹划分成两组——
空组贡献
样例 1:
样例 2:答案
正解
思路
朴素做法:每个导弹二选一,共
关键观察(阈值划分性质):存在最优解,使得"交给 1 号系统的导弹"恰好是
证明:任给一个划分,设 1 号一侧当前
。若存在导弹 满足 却被分到 ,把 挪进 : 的最大 不变( ), 变小后其最大 只会不变或变小,代价不增。反复挪动即得阈值形式;同时这也说明任意最优解中 必等于 中某颗导弹的 (或 )。
降到一维:阈值
消掉重复计算:排序后,前
下表展示样例 2 按
| 分界点 |
前 |
后 |
代价 |
|---|---|---|---|
排序后的点对依次是
代码
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-10 11:30
* update_at: 2026-10-10 11:30
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int x1_, y1_, x2_, y2_;
int n;
vector<pair<ll, ll> > pairs; // (d1, d2) 距离平方
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> x1_ >> y1_ >> x2_ >> y2_;
cin >> n;
for (int i = 0; i < n; i++) {
ll x, y;
cin >> x >> y;
ll d1 = (x - x1_) * (x - x1_) + (y - y1_) * (y - y1_);
ll d2 = (x - x2_) * (x - x2_) + (y - y2_) * (y - y2_);
pairs.push_back(make_pair(d1, d2));
}
sort(pairs.begin(), pairs.end());
// suf[i]:第 i 颗及以后全交给 2 号系统所需的 d2 后缀最大值
vector<ll> suf(n + 1, 0);
for (int i = n - 1; i >= 0; i--)
suf[i] = max(pairs[i].second, suf[i + 1]);
ll ans = 1LL << 62;
for (int k = 0; k <= n; k++) {
ll r1 = (k ? pairs[k - 1].first : 0);
ll cand = r1 + suf[k];
if (cand < ans) ans = cand;
}
cout << ans << "\n";
return 0;
}#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-10-02 08:40
# update_at: 2026-10-02 08:40
import sys
def min_cost(pairs: list[tuple[int, int]]) -> int:
"""枚举分界点 k:前 k 颗交给 1 号系统、其余交给 2 号系统,求 r1^2 + r2^2 的最小值。"""
n = len(pairs)
# suf[i]:把第 i 颗及以后全交给 2 号系统所需的 r2^2,即 d2 的后缀最大值(suf[n] = 0)
suf = [0] * (n + 1)
for i in range(n - 1, -1, -1):
suf[i] = max(pairs[i][1], suf[i + 1])
# pairs 按 d1 升序:前 k 颗交给 1 号系统时 r1^2 就是第 k 颗的 d1;k = 0 表示 1 号不拦
return min((pairs[k - 1][0] if k else 0) + suf[k] for k in range(n + 1))
def solve() -> None:
data = iter(map(int, sys.stdin.buffer.read().split()))
x1, y1, x2, y2 = next(data), next(data), next(data), next(data)
n = next(data)
coords = [next(data) for _ in range(2 * n)] # 之后 2N 个数:每颗导弹的 x, y 交替
# 每颗导弹记为 (d1, d2):到两套系统各自的距离平方
pairs = sorted(
((x - x1) ** 2 + (y - y1) ** 2, (x - x2) ** 2 + (y - y2) ** 2)
for x, y in zip(coords[::2], coords[1::2])
)
print(min_cost(pairs))
if __name__ == "__main__":
solve()复杂度
- 时间:排序
主导,后缀最大值与分界点扫描各 ,总计 。 - 空间:
,存放坐标数组、 数对与后缀数组。
总结
把"每颗导弹二选一"的指数划分,用阈值划分性质(把
