AcWing 121 赶牛入圈(二分,前缀和,离散化,尺取)

题目链接

解题思路

  很明显找半径直接二分最小值就行了,但是由于题目给的坐标太大,不可能开一个\(10000^2\)的二维数组求前缀和来写check函数,所以就需要离散化处理。

代码

const int maxn = 5e2+10;
int c,n,fr[maxn],fc[maxn],pre[maxn][maxn],k_fr,k_fc;
P ori[maxn];
bool check(int x) {
    for (int s = 1; s<k_fr; ++s) {
        //二分找半径之内离枚举的行数最远(指行数差最大)的点
        int e = upper_bound(fr+1,fr+k_fr,fr[s]+x-1)-fr;
        --e;
        int l = 1, r = 1, sum = 0;
        //列移动
        while(l<k_fc) {
            while(r<k_fc && fc[r]-fc[l]+1<=x) {
                sum += pre[e][r]-pre[s-1][r];
                ++r;
            }
            if (sum>=c) return true;
            sum -= pre[e][l]-pre[s-1][l];
            ++l;
        }
    }
    return false;
}
int main() {
    scanf("%d%d",&c,&n);
    for (int i = 1; i<=n; ++i) {
        scanf("%d%d",&fr[i],&fc[i]);
        ori[i] = {fr[i],fc[i]};
    }
    //行、列离散化
    sort(fr+1,fr+n+1); 
    sort(fc+1,fc+n+1);
    k_fr = unique(fr+1,fr+n+1)-fr;
    k_fc = unique(fc+1,fc+n+1)-fc;
    for (int i = 1; i<=n; ++i) {
        int fxx = lower_bound(fr+1,fr+k_fr,ori[i].first)-fr;
        int fyy = lower_bound(fc+1,fc+k_fc,ori[i].second)-fc;
        ++pre[fxx][fyy];
    }
    //求离散化后的前缀和
    for (int i = 1; i<k_fr; ++i)
        for (int j = 1; j<k_fc; ++j)
            pre[i][j] += pre[i-1][j];
    int l = 1, r = 10000;
    while(l<r) {
        int mid = (l+r)>>1;
        if (check(mid)) r = mid;
        else l = mid+1;
    }
    printf("%d\n",l);
    return 0;
}
posted @ 2020-07-15 23:12  shuitiangong  阅读(143)  评论(0编辑  收藏  举报