洛谷P4357 [CQOI2016]K远点对

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 远点对的距离的平方(一定是个整数)。


Solution

K-Dtree板子?
玄学时间复杂度

维护一个小根堆 存最大2m个距离(点对会算两次)
每个点都进K-Dtree算一次
显而易见对于一个点到矩阵 必然点到矩阵的四个角的距离最大
判断最大距离是否大于堆顶或堆未达到2*m个数 若否则退出
每到一个点都判断当前点是否能入堆
一直乱搞到底就完了


Code

#include<bits/stdc++.h>
using namespace std;
const double alpha=0.7;
const int k=2;
struct pt{
	int d[k],w;
}a[200010];
struct data{
	int mx[k],mn[k],sum,ls,rs,sz;
	pt p;
}tree[200010];
int stk[200010];
int b[200010][2],n,m;
priority_queue<long long,vector<long long>,greater<long long> >q;
int ans,tot,word,top,rt;
int operator <(pt u,pt v){
	return u.d[word]<v.d[word];
}
int newnode(){
	if(top) return stk[top--];
	return ++tot;
}
void up(int x){
	int l=tree[x].ls;
	int r=tree[x].rs;
	for(int i=0;i<2;i++){
		tree[x].mx[i]=tree[x].mn[i]=tree[x].p.d[i];
		if(l) tree[x].mx[i]=max(tree[x].mx[i],tree[l].mx[i]);
		if(r) tree[x].mx[i]=max(tree[x].mx[i],tree[r].mx[i]);
		if(l) tree[x].mn[i]=min(tree[x].mn[i],tree[l].mn[i]);
		if(r) tree[x].mn[i]=min(tree[x].mn[i],tree[r].mn[i]);
	}
	tree[x].sum=tree[l].sum+tree[r].sum+tree[x].p.w;
	tree[x].sz=tree[l].sz+tree[r].sz+1;
}
int build(int l,int r,int wd){
	if(l>r) return 0;
	int mid=(l+r)>>1;
	int x=newnode();
	word=wd,nth_element(a+l,a+mid,a+r+1);
	tree[x].p=a[mid];
	tree[x].ls=build(l,mid-1,wd^1);
	tree[x].rs=build(mid+1,r,wd^1);
	up(x); return x;
}
void flt(int x,int num){
	int l=tree[x].ls;
	int r=tree[x].rs;
	if(l) flt(l,num);
	stk[++top]=x;
	a[tree[l].sz+num+1]=tree[x].p;
	if(r) flt(r,num+tree[l].sz+1);
}
void check(int &x,int wd){
	int l=tree[x].ls,r=tree[x].rs;
	if(tree[x].sz*alpha<tree[l].sz||tree[x].sz*alpha<tree[r].sz)
	flt(x,0),x=build(1,tree[x].sz,wd);
}
void ins(int &x,pt y,int wd){
	if(x==0){
		x=newnode();
		tree[x].p=y;
		tree[x].ls=0;
		tree[x].rs=0;
		up(x);
		return;
	}
	if(y.d[wd]<=tree[x].p.d[wd]) ins(tree[x].ls,y,wd^1);
	else ins(tree[x].rs,y,wd^1);
	up(x),check(x,wd);
}
long long work(int x1,int y1,int x2,int y2){
	return (x1-x2)*1ll*(x1-x2)+(y1-y2)*1ll*(y1-y2);
	//return (abs(x1-x2)+abs(y1-y2))*1ll*(abs(x1-x2)+abs(y1-y2));
}
long long get(int x,int y,int X1,int Y1,int X2,int Y2){
	return max(work(x,y,X1,Y1),max(work(x,y,X2,Y2),max(work(x,y,X1,Y2),work(x,y,X2,Y1))));
}
void query(int u,int x,int y){
	//cout<<u<<" "<<x<<" "<<y<<endl;
	if(u==0) return;
	int l=tree[u].ls,r=tree[u].rs;
	if(q.size()==2*m&&get(x,y,tree[u].mn[0],tree[u].mn[1],tree[u].mx[0],tree[u].mx[1])<=q.top()) return;
	//cout<<u<<" "<<x<<" "<<y<<" "<<tree[u].p.d[0]<<" "<<tree[u].p.d[1]<<" "<<work(x,y,tree[u].p.d[0],tree[u].p.d[1])<<" "<<(q.size()?q.top():-1)<<endl;
	if(q.size()<2*m||work(x,y,tree[u].p.d[0],tree[u].p.d[1])>q.top()){
		if(q.size()==2*m) q.pop();
		q.push(work(x,y,tree[u].p.d[0],tree[u].p.d[1]));
	}
//	cout<<l<<" "<<r<<endl;
	if(l&&get(x,y,tree[l].mn[0],tree[l].mn[1],tree[l].mx[0],tree[l].mx[1])>get(x,y,tree[r].mn[0],tree[r].mn[1],tree[r].mx[0],tree[r].mx[1])){
		if(l) query(l,x,y);
		if(r) query(r,x,y);
	}
	else{
		if(r) query(r,x,y);
		if(l) query(l,x,y);
	}
//	cout<<"swihfdriuqw\n";
}
int main(){
	int x,y;
	//freopen("s.txt","r",stdin);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d%d",&a[i].d[0],&a[i].d[1]);
		b[i][0]=a[i].d[0],b[i][1]=a[i].d[1];
	}
	rt=build(1,n,0);
	//for(int i=1;i<=n;i++)
	//cout<<tree[i].p.d[0]<<" "<<tree[i].p.d[1]<<" "<<tree[i].ls<<" "<<tree[i].rs<<" "<<tree[i].mn[0]<<" "<<tree[i].mn[1]<<" "<<tree[i].mx[0]<<" "<<tree[i].mx[1]<<endl;
	for(int i=1;i<=n;i++)
	query(rt,b[i][0],b[i][1]);
	printf("%lld",q.top());
	//while(q.size()) printf("%lld ",q.top()),q.pop();
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值