bzoj 4520: [Cqoi2016]K远点对(KD-tree)

4520: [Cqoi2016]K远点对

Time Limit: 30 Sec   Memory Limit: 512 MB
Submit: 628   Solved: 334
[ Submit][ Status][ Discuss]

Description

已知平面内 N 个点的坐标,求欧氏距离下的第 K 远点对。

Input

输入文件第一行为用空格隔开的两个整数 N, K。接下来 N 行,每行两个整数 X,Y,表示一个点
的坐标。1 < =  N < =  100000, 1 < =  K < =  100, K < =  N*(N−1)/2 , 0 < =  X, Y < 2^31。

Output

输出文件第一行为一个整数,表示第 K 远点对的距离的平方(一定是个整数)。

Sample Input

10 5
0 0
0 1
1 0
1 1
2 0
2 1
1 2
0 2
3 0
3 1

Sample Output

9

HINT

Source

[ Submit][ Status][ Discuss]

题解:KD-tree

建立KD-tree,枚举每个节点更新答案。因为这样每个点对会被计算两次,所以将k*=2

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
#define LL long long 
#define N 200003
using namespace std;
int root,cmpd,n,m,x,y;
struct data
{
	int d[2],mx[2],mn[2],l,r;
}tr[N];
priority_queue<LL,vector<LL>,greater<LL> > p;
int cmp(data a,data b)
{
	return a.d[cmpd]<b.d[cmpd]||a.d[cmpd]==b.d[cmpd]&&a.d[cmpd^1]<b.d[cmpd^1];
}
void update(int now)
{
	int l=tr[now].l;int r=tr[now].r;
	for (int i=0;i<=1;i++){
		if (l) tr[now].mx[i]=max(tr[now].mx[i],tr[l].mx[i]),
		       tr[now].mn[i]=min(tr[now].mn[i],tr[l].mn[i]);
		if (r) tr[now].mx[i]=max(tr[now].mx[i],tr[r].mx[i]),
		       tr[now].mn[i]=min(tr[now].mn[i],tr[r].mn[i]);
	}
}
int build(int l,int r,int d)
{
	 cmpd=d;
	 int mid=(l+r)/2;
	 nth_element(tr+l,tr+mid,tr+r+1,cmp);
	 for (int i=0;i<=1;i++)
	  tr[mid].mx[i]=tr[mid].mn[i]=tr[mid].d[i];
	 if (l<mid) tr[mid].l=build(l,mid-1,d^1);
	 if (r>mid) tr[mid].r=build (mid+1,r,d^1);
	 update(mid);
	 return mid;
}
LL pow(int x)
{
	return (LL)x*(LL)x;
}
LL dist(int now)
{
	LL t=0;
	t+=max(pow(tr[now].mx[0]-x),pow(tr[now].mn[0]-x));
	t+=max(pow(tr[now].mx[1]-y),pow(tr[now].mn[1]-y));
	return t;
}
void query(int now)
{
	LL d0=pow(tr[now].d[0]-x)+pow(tr[now].d[1]-y);
	if (p.size()<m) p.push(d0);
	else {
		LL t=p.top();
		if (d0>t) {
			p.pop();
			p.push(d0);
		}		
	}
	LL dl=-1,dr=-1;
	if(tr[now].l) dl=dist(tr[now].l);
	if(tr[now].r) dr=dist(tr[now].r);
	if (dl>dr) {
		if (dl!=-1)
		 if (p.size()<m||p.top()<dl) query(tr[now].l);
		if (dr!=-1)
		 if (p.size()<m||p.top()<dr) query(tr[now].r);
	}
	else{
		if (dr!=-1)
		 if (p.size()<m||p.top()<dr) query(tr[now].r);
		if (dl!=-1)
		 if (p.size()<m||p.top()<dl) query(tr[now].l);
	}
}
int main()
{
	freopen("a.in","r",stdin);
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++) scanf("%d%d",&tr[i].d[0],&tr[i].d[1]);
	root=build(1,n,0);
	m*=2;
	for (int i=1;i<=n;i++) {
		x=tr[i].d[0]; y=tr[i].d[1];
		query(root);
	}
	printf("%I64d\n",p.top());
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值