题解与算法分析
牛在 $y_c$,目标点 $(x, y)$,斜率 $s$。
方程:$$y_c = y - s \cdot x$$
题目最大的难点在于意识到:上边界和下边界是分开计算的。
但是每个目标左侧边界 ($x_1$) 上的点 $(X_1, y_1[i])$ 和 $(X_1, y_2[i])$ 怎么办?
$\rightarrow$ 贪心分配!
为了让 $y_{\max}$ 尽可能小,$y_{\min}$ 尽可能大(压缩范围):
以正斜率组为例:
由于斜率是整数,答案具有单调性。
multiset 维护可用斜率,upper_bound 查找并删除。复杂度:$O(N \log (\text{Range}) \cdot \log N) \approx O(N \log^2 N)$
不需要重写逻辑!利用对称性:
$$y_c = y - s \cdot x \iff (-y_c) = (-y) - (-s) \cdot x$$
solve() 函数。最终答案:$Ans_{total} = (-Ans_{neg}) - Ans_{pos}$
bool check(vector &pts, vector &slopes, long long y) {
multiset<int> ss;
for (auto s: slopes) ss.insert(s);
for (auto p: pts) {
// 计算该点允许的最大斜率
long long s0 = (p.second - y) / p.first;
// 在集合中找 <= s0 的最大斜率
auto it = ss.lower_bound(s0);
if (it == ss.end() || *it > s0) {
if (it == ss.begin()) return false; // 没找到
it--;
}
ss.erase(it); // 贪心匹配,删掉这个斜率
}
return true;
}
// 求解在给定点集和斜率集下,牛的 y 坐标最大可能是多少
ll solve(vector<pi> &pts, vector<int> &slopes) {
long long l = -2e14, r = 2e14; // 注意范围
long long ans = -2e14;
while (l <= r) {
long long mid = (l + r) / 2; // 向下取整
// C++整除负数有时行为怪异,最好用 floor 或手写 (l+r)>>1
// 这里为了演示简单展示逻辑:
if (l+r < 0 && (l+r)%2 != 0) mid = (l+r-1)/2;
if (check(pts, slopes, mid)) {
ans = mid;
l = mid + 1; // 尝试推高 y
} else {
r = mid - 1;
}
}
return ans;
}
// 1. 分离正负斜率
for (int i=0; i<4*n; i++) {
if (s[i] > 0) slopes[0].push_back(s[i]);
else slopes[1].push_back(-s[i]); // 存为正数方便复用
}
// 2. 必须的点 (TR -> pos, BR -> neg)
for (int i=0; i<n; i++) {
pts[0].push_back({x2[i], y1[i]}); // y1是较大的那个? 题目y1,y2需确认大小
pts[1].push_back({x2[i], -y2[i]}); // 取反
}
// 3. 左侧点排序分配
vector<int> y_all;
for (int i=0; i<n; i++) { y_all.push_back(y1[i]); y_all.push_back(y2[i]); }
sort(y_all.begin(), y_all.end());
for (auto y: y_all) {
if (pts[1].size() < 2*n) pts[1].push_back({x1, -y}); // 给负斜率组(取反)
else pts[0].push_back({x1, y}); // 给正斜率组
}