P7883 平面最近点对(加强加强版) 题解
前言
现在的题解除了随机算法外只有两篇分治做法,一篇神仙用的蒟蒻看不懂的 vector,另一篇是
解题思路
先按照
归并时按照
然后步入难点:
假设我们已经求出了左半部分的最近距离和右半部分的最近距离,两个距离的较小值设为
我们把划分左右两部分的中线的
很显然,最终的答案有三种情况:
- 两点在左半部分
- 两点在右半部分
- 一个点在左边,一个点在右边(即跨过中线)
我们已经求出了
对于第三种情况,只有距离中线的距离
所以我们在归并后,枚举一遍,找出所有
然后枚举一遍
- 这个点上方的点
- 这个点下放的点
而且这两个点的
而我们刚刚的归并排序就是按照
对于时间复杂度,可以证明符合条件的点很少(最多
所以总复杂度为
AC代码
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<iomanip>
using namespace std;
const int maxn=400005;
int n;
struct Point{
long long x,y;
}p[maxn],q[maxn];
bool cmp(const Point &a,const Point &b){
return a.x<b.x;
}
long long dis(const Point &a,const Point &b){
return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
long long divide(int l,int r){
if(l==r) return 1ll<<62;
int mid=(l+r)/2;
long long midx=p[mid].x;
long long d=min(divide(l,mid),divide(mid+1,r));
int p1=l,p2=mid+1,tot=0;
while(p1<=mid||p2<=r){
if(p1<=mid&&(p2>r||p[p1].y<p[p2].y)){
q[++tot]=p[p1++];
}else{
q[++tot]=p[p2++];
}
}
for(int i=1;i<=tot;i++){
p[l+i-1]=q[i];
}
tot=0;
long long dd=d;
d=sqrt(dd);
for(int i=l;i<=r;i++){
if(abs(p[i].x-midx)<=d) q[++tot]=p[i];
}
for(int i=1;i<=tot;i++){
for(int j=i-1;j>=1&&q[i].y-q[j].y<=d;j--){
dd=min(dd,dis(q[i],q[j]));
d=sqrt(dd);
}
}
return dd;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++){
scanf("%lld%lld",&p[i].x,&p[i].y);
}
sort(p+1,p+n+1,cmp);
cout<<divide(1,n)<<endl;
return 0;
}