[CQOI2016]K 远点对

发布时间 2023-03-23 16:53:49作者: PPXppx

洛谷

题意:已知平面内 \(N\) 个点的坐标,求欧氏距离下的第 \(K\) 远点对。两个点 \(P(x_1,y_1)\)\(Q(x_2,y_2)\) 的欧氏距离定义为 \(\sqrt{(x_1-x_2)^2+(y_1-y_2)^2}\)

分析:按照\(KD-Tree\)的方式对这n个点建树,建立一个小根堆,维护当前最远的k个点对距离,但是因为如果\(dis(i,j)\)在堆中,那么\(dis(j,i)\)也要在堆中,因此实际上堆的大小是\(2k\),最后的答案就是堆顶的值。建树之后,遍历每一个点(以下称为当前节点)去看能不能更新堆。对于每一棵子树,首先判断根节点与当前节点之间的距离能否更新堆,然后对于该子树的左右儿子节点,根据\(KD-Tree\)的性质,每一个节点维护了一个长方形的信息(一棵子树包含了若干个节点,这个长方形是最小的能够包括所有子树中节点的长方形,也就是建树时记录了每个子树中所有节点横坐标和纵坐标的最值),当前节点到这个长方形的最远距离一定是到四个顶点的距离之一,如果最远距离都不能更新堆顶,那就没必要递归这棵子树了。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int N=1e5+5;
const int M=2e4+5;
const int mod=1e9+7;
int n,k;
int l[N],r[N],d[N],u[N],ls[N],rs[N];
int divi[N];
priority_queue<ll,vector<ll>,greater<ll> >q;
struct node{
	int x,y;
}a[N];
void update(int p){
	l[p]=r[p]=a[p].x;d[p]=u[p]=a[p].y;
	if(ls[p]){
		l[p]=min(l[p],l[ls[p]]);r[p]=max(r[p],r[ls[p]]);
		d[p]=min(d[p],d[ls[p]]);u[p]=max(u[p],u[ls[p]]);
	}
	if(rs[p]){
		l[p]=min(l[p],l[rs[p]]);r[p]=max(r[p],r[rs[p]]);
		d[p]=min(d[p],d[rs[p]]);u[p]=max(u[p],u[rs[p]]);
	}
}
bool cmp1(node x,node y){
	return x.x<y.x;
}
bool cmp2(node x,node y){
	return x.y<y.y;
}
int build(int l,int r){
	if(l>r)return 0;
	int mid=(l+r)>>1;
	double avx=0,avy=0,fcx=0,fcy=0;
	for(int i=l;i<=r;++i){
		avx+=a[i].x;avy+=a[i].y;
	}
	avx=avx*1.0/(r-l+1);avy=avy*1.0/(r-l+1);
	for(int i=l;i<=r;++i){
		fcx+=(avx-a[i].x)*(avx-a[i].x);
		fcy+=(avy-a[i].y)*(avy-a[i].y);
	}
	if(fcx>=fcy){
		nth_element(a+l,a+mid,a+r+1,cmp1);
		divi[mid]=1;
	}
	else{
		nth_element(a+l,a+mid,a+r+1,cmp2);
		divi[mid]=2;
	}
	ls[mid]=build(l,mid-1);
	rs[mid]=build(mid+1,r);
	update(mid);
	return mid;
}
ll calc(int i,int j){
	return 1ll*(a[i].x-a[j].x)*(a[i].x-a[j].x)+1ll*(a[i].y-a[j].y)*(a[i].y-a[j].y);
}
ll dist(int i,int j){
	ll t1=max(1ll*(a[i].x-l[j])*(a[i].x-l[j]),1ll*(a[i].x-r[j])*(a[i].x-r[j]));
	ll t2=max(1ll*(a[i].y-d[j])*(a[i].y-d[j]),1ll*(a[i].y-u[j])*(a[i].y-u[j]));
	return t1+t2;
}
void query(int l,int r,int x){
	if(l>r)return;
	int mid=(l+r)>>1;
	ll t=calc(x,mid);
	if(t>q.top()){
		q.pop();
		q.push(t);
	}
	ll t1=dist(x,ls[mid]),t2=dist(x,rs[mid]);
	if(t1>q.top()&&t2>q.top()){
		if(t1>t2){
			query(l,mid-1,x);
			if(t2>q.top())query(mid+1,r,x);
		}
		else{
			query(mid+1,r,x);
			if(t1>q.top())query(l,mid-1,x);
		}
	}
	else{
		if(t1>q.top())query(l,mid-1,x);
		if(t2>q.top())query(mid+1,r,x);
	}
}
int main() {
	n=read();k=read();
	k<<=1;
	for(int i=1;i<=k;++i)q.push(0);
	for(int i=1;i<=n;++i){
		a[i].x=read();a[i].y=read();
	}
	build(1,n);
	for(int i=1;i<=n;++i)query(1,n,i);
	cout<<q.top()<<endl;
    return 0; 
}