CodeForces 314 D Sereja and Straight Lines
传送门
-
题意
再平面内有\(n\)个点,两个夹角为\(90\)°并与\(x\)轴夹角为\(45\)°的直线,求所有点与直线中的点的曼哈顿距离中的最大值最小。
-
思路
既然提到了最大值最小,那么必然是二分无疑了,又因为直线与坐标轴夹角为\(45\)°,所以曼哈顿距离就为点到直线距离的\(\sqrt{2}\)倍。我们把整张图旋转\(45\)°,并且放大\(\sqrt{2}\)倍,点\((x,y)\)就变为\((x-y,x+y)\)(证明略),这样就避免了小数的出现。又因为旋转后两条直线就变成了一个十字,点与直线的最小距离就变为与横着的直线或竖着的直线的距离的最小值。然后对\(n\)个点以横坐标排序,二分最大距离,枚举横着能包含的区间,剩下的点再判断是否能用竖着的区间包括(具体可以看代码)。
-
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
struct ab
{
int x,y;
} t[500005];
int maxl[500005],minl[500005],maxr[500005],minr[500005],n;
int cmp(ab x,ab y)
{
return x.x<y.x;
}
int work(int x)
{
int h=1;
for(int i=1;i<=n;i++)//枚举横着的区间
{
for(;h<=n;h++)
{
if(t[h].x-t[i].x>x) break;
}
if(max(maxl[i-1],maxr[h])-min(minl[i-1],minr[h])<=x) return 1;//判断竖着的区间
}
return 0;
}
signed main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
int x,y;
scanf("%lld%lld",&x,&y);
t[i].x=x-y;//旋转
t[i].y=x+y;
}
sort(t+1,t+1+n,cmp);
maxl[0]=-2e10;
minl[0]=2e10;
for(int i=1;i<=n;i++)//预处理纵坐标的最大最小值,判断竖着的区间时有用
{
maxl[i]=max(maxl[i-1],t[i].y);
minl[i]=min(minl[i-1],t[i].y);
}
maxr[n+1]=-2e10;
minr[n+1]=2e10;
for(int i=n;i>=1;i--)//预处理纵坐标的最大最小值,判断竖着的区间时有用
{
maxr[i]=max(maxr[i+1],t[i].y);
minr[i]=min(minr[i+1],t[i].y);
}
int l=0,r=1e10;
while(l<r)//二分
{
int mid=(l+r)/2;
if(!work(mid)) l=mid+1;
else r=mid;
}
printf("%lf\n",(double)r/2);//注意二分的是区间的大小,所以答案还要除以二才是距离
return 0;
}