2
赶牛入圈
思路
这道题问最小边长,直接做比较难做,这时应该想到二分。在此之前我们先证明这个正方形至少有 \(3\) 条边上有点。如果只有两条边,一定能通过平移使得其满足。但是这题有一个很棘手的地方,点的范围是 \([1,10000]\),但是用到的点只有 \(500\) 个,坐标最多就 \(1000\) 个,所以应该考虑离散化,而且这里的离散化还需要注意绝对和相对的区别,原来两个数差很多,可能被映射成差 \(1\),为了避免诸如此类的情况,可以考虑用一种类似于双指针的思想,先枚举矩阵的右下角(上文的引理),然后找到第一个不在二分的 \(mid\) 范围内的最上方的点(注意方形与点不同,需要多 \(1\) 的坐标才能圈住),由于枚举右下角时是单调的,所以那个点也是单调的,可以在与枚举右下角相同的复杂度算出,然后用预处理的前缀和 \(O(1)\)。最坏会有 \(1000\) 个坐标,复杂度是 \(O(1000^2\log 10000)\),当然如果将横纵坐标分别离散化,可以优化成 \(O(500^2\log 10000)\)。还有一点,这题其实并不需要在乎是左上角、右上角、左下角、右下角,因为可以看作整体平移。
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int c,n,a[N],cnt,sum[N][N],X[N],Y[N];
int find(int x){
int l=0,r=cnt;
while(l<r){
int mid=l+r>>1;
if(a[mid]>=x)r=mid;
else l=mid+1;
}
return r;
}
bool check(int len){
for(int x1=0,x2=1;x2<=cnt;++x2){
while(a[x2]-a[x1+1]+1>len)++x1;
for(int y1=0,y2=1;y2<=cnt;++y2){
while(a[y2]-a[y1+1]+1>len)++y1;
if(sum[x2][y2]-sum[x1][y2]-sum[x2][y1]+sum[x1][y1]>=c)return 1;
}
}
return 0;
}
int main(){
scanf("%d%d",&c,&n);
for(int i=1;i<=n;++i){
int x,y;
scanf("%d%d",&x,&y);
a[++cnt]=x,a[++cnt]=y;
X[i]=x,Y[i]=y;
}
sort(a+1,a+1+cnt);
cnt=unique(a+1,a+1+cnt)-a-1;
for(int i=1;i<=n;++i){
int xx=find(X[i]),yy=find(Y[i]);
++sum[xx][yy];
}
for(int i=1;i<=cnt;++i)
for(int j=1;j<=cnt;++j)sum[i][j]+=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
int l=1,r=10000;
while(l<r){
int mid=l+r>>1;
if(check(mid))r=mid;
else l=mid+1;
}
printf("%d",r);
return 0;
}
自己想的思路前面和上面大体一致,枚举左上角,但是直接用二分确定右下角,多一只 \(\log\)。
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1010;
int c,n,a[N],sum[N][N],nn;
struct point{
int x,y;
}p[N];
int find(int x){
int l=0,r=n;
while(l<r){
int mid=l+r+1>>1;
if(a[mid]<=x)l=mid;
else r=mid-1;
}
return r;
}
bool check(int mid){
int res=0;
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j){
int maxx=find(a[i]+mid-1),maxy=find(a[j]+mid-1);
res=max(sum[maxx][maxy]-sum[maxx][j-1]-sum[i-1][maxy]+sum[i-1][j-1],res);
}
return res>=c;
}
int main(){
scanf("%d%d",&c,&n);
nn=n;
int minx=1e9,miny=1e9,maxx=0,maxy=0;
for(int i=1;i<=n;++i){
int x,y;
scanf("%d%d",&x,&y);
p[i]={x,y};
a[(i<<1)-1]=x,a[i<<1]=y;
minx=min(minx,x),miny=min(miny,y),maxx=max(maxx,x),maxy=max(maxy,y);
}
sort(a+1,a+(n<<1|1));
n=unique(a+1,a+(n<<1|1))-a-1;
for(int i=1;i<=nn;++i){
int x=lower_bound(a+1,a+1+n,p[i].x)-a,y=lower_bound(a+1,a+1+n,p[i].y)-a;
++sum[x][y];
}
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
sum[i][j]+=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
int l=0,r=max(maxx-minx+1,maxy-miny+1);
while(l<r){
int mid=l+r>>1;
if(check(mid))r=mid;
else l=mid+1;
}
printf("%d",r);
return 0;
}