FOJ Corral the Cows 题解
题目描述
约翰打算建一个围栏来圈养他的奶牛。作为最挑剔的兽类,奶牛们要求这个围栏必须是正方形的,而且围栏里至少要有C个草场,来供应她们的午餐。
约翰的土地上共有N个草场,每个草场在一块1×1的方格内。有时候,会有多个草场在同一个方格内,那他们的坐标就会相同。
告诉约翰,最小的围栏的边长是多少?
输入
Line 1: Two space-separated integers: C and N.
Lines 2..N+1: Each line contains two space-separated integers that are the X,Y coordinates of a clover field.
输出
Line 1: A single line with a single integer that is length of one edge of the minimum size square that contains at least C clover fields.
样例输入
3 4
1 2
2 1
4 1
5 2
样例输出
4
数据范围
分析
可以发现若围栏的边长r作为自变量,最多能围住的草场数量Smax与r成单调递增关系。于是考虑二分r。
此时问题就转化为:给定边长为r的正方形边框,求围住操场数量的最大值Smax。
如下图,我们分析所有能围住红点的边长为4的正方形(如图中3个蓝色正方形)。
发现它们有共同特征:右下角坐标在以红点为左上角、边长为4的正方形(记为正方形SQ)内。换句话说,整个红色正方形就是蓝色正方形右下角坐标的运动轨迹。
那么,对于很多个点:
取它们的SQ的重叠部分(图中阴影)中的任意一点作为边框的右下角,都可以围住全部三个点。
那么问题就转化为:求若干个正方形的最大重叠次数。
这就与扫描线求矩形面积并十分相似了。线段树可以解决。加上最开始的二分边框边长,总时间复杂度为。
Code
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
struct node {
int x, y1, y2, v;
bool operator <(node other) const {
return x < other.x || x == other.x && v > other.v;
}
} a[2 * N];
struct tree {
int l, r;
int dat, lazy;
} t[8 * N];
int c, n;
int x[N], y[N];
void Build(int p, int l, int r) {
t[p].l = l; t[p].r = r; t[p].dat = t[p].lazy = 0;
if (t[p].l == t[p].r) return ;
int mid = (l + r) >> 1;
Build(p * 2, l, mid); Build(p * 2 + 1, mid + 1, r);
}
void PushDown(int p) {
if(!t[p].lazy) return ;
t[p * 2].dat += t[p].lazy;
t[p * 2 + 1].dat += t[p].lazy;
t[p * 2].lazy += t[p].lazy;
t[p * 2+ 1].lazy += t[p].lazy;
t[p].lazy = 0;
}
void change(int p, int l, int r, int v) {
if (l <= t[p].l && t[p].r <= r) {
t[p].dat += v; t[p].lazy += v; return ;
}
PushDown(p);
int mid = (t[p].l + t[p].r) >> 1;
if(l <= mid) change(p * 2, l, r, v);
if (r > mid) change(p * 2 + 1, l, r, v);
t[p].dat = max(t[p * 2].dat, t[p * 2 + 1].dat);
}
bool check(int r) {
for (int i = 1; i <= n; i++) {
a[2 * i - 1] = (node){x[i] - r + 1, y[i], y[i] + r - 1, 1};
a[2 * i] = (node){x[i], y[i], y[i] + r - 1, -1};
}
sort(a + 1, a + 1 + 2 * n);
Build(1, 1, 2e5);
for (int i = 1; i <= 2 * n; i++) {
change(1, a[i].y1, a[i].y2, a[i].v);
if (t[1].dat >= c) return 1;
}
return 0;
}
int main() {
scanf("%d%d", &c, &n);
for (int i = 1; i <= n; i++)
scanf("%d%d", &x[i], &y[i]);
int l = 1, r = 1e5, mid;
while (l < r) {
mid = (l + r) >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
printf("%d\n", l);
return 0;
}
本文作者:realFish的博客
本文链接:https://www.cnblogs.com/fish07/p/15848222.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步