CQOI 2016 K远点对 题解

题目传送门

题目大意: 给出 n n n 个点,求出其中距离第 k k k 远的点对距离。

题解

建出K-D tree,然后造一个大小为 2 k 2k 2k 的小根堆,将每个点丢进K-D tree中跑一遍更新这个堆即可。

代码如下:

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
#define inf 999999999999999999ll
#define maxn 100010
#define ll long long
#define zuo ch[0]
#define you ch[1]
#define pf(x) ((x)*(x))

int n,k;
priority_queue< ll,vector<ll>,greater<ll> >q;
const int K=2;
struct point{ll d[K];}a[maxn];
int C;bool cmp(point x,point y){return x.d[C]<y.d[C];}
struct KD_node *root=NULL,*null=NULL;
struct KD_node{
	point x,ld,ru;
	KD_node *ch[2];
	
	KD_node(point X):x(X),ld(X),ru(X){ch[0]=ch[1]=null;}
	KD_node(){}
	
	void check(){
		for(int i=0;i<K;i++){
			ld.d[i]=min(ld.d[i],min(zuo->ld.d[i],you->ld.d[i]));
			ru.d[i]=max(ru.d[i],max(zuo->ru.d[i],you->ru.d[i]));
		}
	}
	ll dis_max(point X){
		if(this==null)return -inf;ll re=0;
		for(int i=0;i<K;i++)re+=pf(max( abs(X.d[i]-ld.d[i]) , abs(X.d[i]-ru.d[i]) ));
		return re;
	}
};
void init(){
	null=new KD_node();
	for(int i=0;i<K;i++){
		null->ld.d[i]=inf;
		null->ru.d[i]=-inf;
	}
}
void build_KDtr(KD_node *&now,int l,int r,int CO=0)
{
	if(l>=r)return;
	int mid=l+r>>1;
	C=CO;nth_element(a+l,a+mid,a+r,cmp);
	now=new KD_node(a[mid]);
	build_KDtr(now->zuo,l,mid,(CO+1)%K);
	build_KDtr(now->you,mid+1,r,(CO+1)%K);
	now->check();
}
ll dis(point x,point y){
	ll re=0;
	for(int i=0;i<K;i++)re+=pf(x.d[i]-y.d[i]);
	return re;
}
void go(KD_node *now,point x)
{
	if(now==null)return;
	ll c=q.top();q.pop();
	c=max(c,dis(now->x,x));q.push(c);
	ll dis[2]={ now->zuo->dis_max(x) , now->you->dis_max(x) },to=(dis[0]>dis[1])^1;
	if(dis[to]>q.top())go(now->ch[to],x);
	if(dis[to^1]>q.top())go(now->ch[to^1],x);
}

int main()
{
	scanf("%d %d",&n,&k);
	for(int i=1;i<=2*k;i++)q.push(-inf);
	for(int i=1;i<=n;i++){
		for(int j=0;j<K;j++)
		scanf("%lld",&a[i].d[j]);
	}
	init();build_KDtr(root,1,n+1);
	for(int i=1;i<=n;i++)go(root,a[i]);
	printf("%lld",q.top());
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值