题干:
农夫约翰希望为他的奶牛们建立一个畜栏。
这些挑剔的畜生要求畜栏必须是正方形的,而且至少要包含C单位的三叶草,来当做它们的下午茶。
畜栏的边缘必须与X,Y轴平行。
约翰的土地里一共包含N单位的三叶草,每单位三叶草位于一个1 x 1的土地区域内,区域位置由其左下角坐标表示,并且区域左下角的X,Y坐标都为整数,范围在1到10000以内。
多个单位的三叶草可能会位于同一个1 x 1的区域内,因为这个原因,在接下来的输入中,同一个区域坐标可能出现多次。
只有一个区域完全位于修好的畜栏之中,才认为这个区域内的三叶草在畜栏之中。
请你帮约翰计算一下,能包含至少C单位面积三叶草的情况下,畜栏的最小边长是多少。
1≤C≤500
C≤N≤500
思路:
首先考虑存储问题,坐标范围[1,10000],用二维数组容易炸,而且不方便遍历,而点的个数只有500,所以我们可以把数据先离散化一下。
然后为了快速求出范围内三叶草的单位数,运用前缀和的知识,因为数据离散化过,所以map[i][j]+=map[i-1][j]+map[i][j-1]-map[i-1][j-1](map[i][j]代表以(i,j)做为右下角的矩形面积);
原理如图:
然后求长度,因为求得是满足条件的最小长度,所以我们对结果二分。
如果当前长度满足条件我们则二分左区间(找最小值嘛),否则二分右区间。
最后考虑二分的判断条件,我们需要确定两个点,一个左上角的点,一个右下角的点。然后因为数据离散化过,所以我们要保证两点之间的长度不能大于当前二分的长度。最后根据求前缀和的思路求出区域内三叶草的值。
注意的点:
①三叶草是1*1的小正方形,我们默认当前的点为正方形的右下角,那么我们最后在算长度的时候注意+1(即-(-1))。
②因为离散化需要一个找位置的过程,所以我们可以直接将x,y都放进一个容器中去排序后去重,然后(x,y)放进一个容器中方便找到离散化的值。
③注意三叶草可以重叠
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <iostream>
#include <algorithm>
#define ll long long
using namespace std;
int n,c;
int map[1010][1010];
vector<pair<int,int> > poi;
vector<int> num;
int find(int t)
{
return lower_bound(num.begin(),num.end(),t)-num.begin();
}
bool check(int len){
int x1=1,x2=1,y1=1,y2=1;
for(;x2<num.size();x2++){
while(num[x2]-num[x1]+1>len) x1++;
for(y1=1,y2=1;y2<num.size();y2++){
while(num[y2]-num[y1]+1>len) y1++;
//printf("%d %d %d %d\n",x2,y2,x1,y1);
//printf("%d\n",map[x2][y2]-map[x1-1][y2]-map[x2][y1-1]+map[x1-1][y1-1]);
if(map[x2][y2]-map[x1-1][y2]-map[x2][y1-1]+map[x1-1][y1-1]>=c)
return true;
}
}
return false;
}
int main()
{
int x,y;
scanf("%d%d",&c,&n);
num.push_back(0);
for(int i=0;i<n;i++){
scanf("%d%d",&x,&y);
poi.push_back({x,y});
num.push_back(x);
num.push_back(y);
}
sort(num.begin(),num.end());
num.erase(unique(num.begin(),num.end()),num.end());
// for(int i=0;i<num.size();i++){
// printf("%d ",num[i]);
// }
// cout<<endl;
for(int i=0;i<n;i++){
x=find(poi[i].first);
y=find(poi[i].second);
map[x][y]++;
}
for(int i=1;i<num.size();i++){
for(int j=1;j<num.size();j++){
map[i][j]+=map[i-1][j]+map[i][j-1]-map[i-1][j-1];
//printf("%d ",map[i][j]);
}
//cout<<endl;
}
int l=1,r=10100;
while(l<r){
int mid=(r+l)>>1;
if(check(mid))
r=mid;
else
l=mid+1;
//printf("%d %d\n",l,r);
}
printf("%d\n",r);
return 0;
}