打打打打打字机|

realFish

园龄:3年1个月粉丝:3关注:0

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
数据范围
N105,X,Y105

分析

可以发现若围栏的边长r作为自变量,最多能围住的草场数量Smax与r成单调递增关系。于是考虑二分r。
此时问题就转化为:给定边长为r的正方形边框,求围住操场数量的最大值Smax

如下图,我们分析所有能围住红点的边长为4的正方形(如图中3个蓝色正方形)。
image
发现它们有共同特征:右下角坐标在以红点为左上角、边长为4的正方形(记为正方形SQ)内。换句话说,整个红色正方形就是蓝色正方形右下角坐标的运动轨迹。
那么,对于很多个点:
image
取它们的SQ的重叠部分(图中阴影)中的任意一点作为边框的右下角,都可以围住全部三个点。
那么问题就转化为:求若干个正方形的最大重叠次数。
这就与扫描线求矩形面积并十分相似了。线段树可以O(NlogN)解决。加上最开始的二分边框边长,总时间复杂度为O(NlogN·logN)


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 中国大陆许可协议进行许可。

posted @   realFish  阅读(36)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起