【二分最小面积 正方形覆盖离散点】 赶牛入圈

传送门

题意

建立畜栏,畜栏的形状必须是正方形,有\(n\)个三叶草,为\(1 \times 1\)的正方形,用左下角的坐标\((x,y)\)来代表,可能会有覆盖,
求至少包含\(c\)个三叶草的情况下求包含所有三叶草的最小畜栏的边长

数据范围

\(1\leq c\leq 500\)
\(c\leq n\leq 500\)
\(1\leq x,y\leq 10^{4}\)

题解

因为一定是正方形,二分正方形的边长

  • 性质:在最优解中,正方形的四个顶点中中至少有两个顶点是有点的,否则,就可以缩小正方形

二维前缀和统计正方形内的点的个数,最坏枚举\((10^{4})^{2}\),大概是\(10^{8}\),但是点的个数只有\(500\)个,离散化后再计算,
离散化后在坐标区间内从小到大遍历并判断是否存在该点,保证了离散化后映射到的原始数据是从小到大排序的
\(Check\)只需要枚举所有点,判断作为正方形的右上角是否合法,如果合法再利用前缀和判断点的个数是否满足要求

Code

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<n;i++)
const int MAX=1e4+10,N=5e2+10;
int hx[MAX],hy[MAX];
int n,c;
int x[N],y[N],s[N][N];
int cntx,cnty;
struct node{
    int x,y;
}a[N];
bool check(int edge)//二分的值是边长
{

    for(int i=hx[edge];i<=cntx;i++)
        for(int j=hy[edge];j<=cnty;j++)
        {
            int x0=0,y0=0;

            if(x[i]-edge+1>=0) x0=hx[x[i]-edge];
            if(y[j]-edge+1>=0) y0=hy[y[j]-edge];
            
            if(s[i][j]-s[x0][j]-s[i][y0]+s[x0][y0]>=c)
                return 1;
        }
    return 0;
}
int main(){
    memset(hx,0,sizeof hx);
    memset(hy,0,sizeof hy);
    memset(s,0,sizeof s);
    cin>>c>>n;

    rep(i,1,n+1)
    {
        cin>>a[i].x>>a[i].y;
        hx[a[i].x]++;
        hy[a[i].y]++;
    }

    rep(i,1,10000+1)
    {
        if(hx[i])
            x[++cntx]=i;
        hx[i]=cntx; //x离散化后的下标

        if(hy[i])
            y[++cnty]=i;
        hy[i]=cnty;//y离散化后的下标
    }

    rep(i,1,n+1)
        s[hx[a[i].x]][hy[a[i].y]]++;

    rep(i,1,cntx+1)
        rep(j,1,cnty+1)
            s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+s[i][j];

    int l=1,r=10000;
    while(l<r)
    {
        int mid=l+r>>1; //最小值 合法 r=mid
        if(check(mid))
            r=mid;
        else
            l=mid+1;
    }
    cout<<l<<endl;
    return 0;
}
posted @ 2020-05-26 09:33  Hyx'  阅读(232)  评论(0编辑  收藏  举报