把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【BZOJ4520】[CQOI2016] K远点对(KD-Tree)

点此看题面

大致题意: 求平面内第\(k\)远点对欧几里得距离的平方。

大致思路

我觉得可以先去看看这题:【BZOJ1941】[SDOI2010] Hide and Seek。和本题相比还要多求一个最小值。。。

根据上面这道题的思想,我们枚举一个起点,然后在\(KD-Tree\)中询问前\(k\)大的距离。

考虑开一个小根堆,初始状态下里面放上\(2k\)\(0\)(之所以是\(2k\),因为一条路径会分别以两个端点为起点计算两次)。

我们每次只需把当前距离和小根堆堆顶比较,就能判断这一距离是否能产生贡献了。

其余部分以及具体实现和上面这题基本差不多。(实际上我懒得再打一遍,就直接把上面的代码复制下来稍微改了改。。。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define LL long long
using namespace std;
int n,k,D;priority_queue<LL,vector<LL>,greater<LL> > Q;
struct Point
{
	int x[2];I int operator [] (CI d) Con {return x[d];}
	I bool operator < (Con Point& o) Con {return x[D]^o.x[D]?x[D]<o.x[D]:x[D^1]<o.x[D^1];}
}p[N+5];
class FastIO
{
	private:
		#define FS 100000
		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
		#define D isdigit(c=tc())
		char c,*A,*B,FI[FS];
	public:
		I FastIO() {A=B=FI;}
		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
		#undef D
}F;
class KDTree//KD-Tree
{
	private:
		#define Gmax(A,B) (A[0]<B[0]&&(A.x[0]=B[0]),A[1]<B[1]&&(A.x[1]=B[1]))
		#define Gmin(A,B) (A[0]>B[0]&&(A.x[0]=B[0]),A[1]>B[1]&&(A.x[1]=B[1]))
		#define PU(x)\
		(\
			Mx(x)=V(x),S(x,0)&&Gmax(Mx(x),Mx(S(x,0))),S(x,1)&&Gmax(Mx(x),Mx(S(x,1))),\
			Mn(x)=V(x),S(x,0)&&Gmin(Mn(x),Mn(S(x,0))),S(x,1)&&Gmin(Mn(x),Mn(S(x,1)))\
		)//上传信息
		int rt,Nt;struct node
		{
			#define V(x) O[x].Val
			#define S(x,d) O[x].Son[d]
			#define Mx(x) O[x].Max
			#define Mn(x) O[x].Min
			int Son[2];Point Val,Max,Min;
		}O[N+5];
		I void Build(CI l,CI r,int& rt,Point *P,CI d=0)//建树
		{
			int mid=l+r>>1;D=d,nth_element(P+l+1,P+mid+1,P+r+1),V(rt=++Nt)=P[mid],
			l<mid?(Build(l,mid-1,S(rt,0),P,d^1),0):(S(rt,0)=0),
			r>mid?(Build(mid+1,r,S(rt,1),P,d^1),0):(S(rt,1)=0),PU(rt);
		}
		#define D(A,B) (1LL*(A[0]-B[0])*(A[0]-B[0])+1LL*(A[1]-B[1])*(A[1]-B[1]))//两点间的欧几里得距离的平方
		#define MxD(x,P)\
			max(1LL*(P[0]-Mn(x)[0])*(P[0]-Mn(x)[0]),1LL*(P[0]-Mx(x)[0])*(P[0]-Mx(x)[0]))+\
			max(1LL*(P[1]-Mn(x)[1])*(P[1]-Mn(x)[1]),1LL*(P[1]-Mx(x)[1])*(P[1]-Mx(x)[1]))//最大距离
		I void Max(CI rt,Con Point& P)//询问子树更新堆的答案
		{
			if(!rt||MxD(rt,P)<=Q.top()) return;LL t=D(V(rt),P);Q.top()<t&&(Q.pop(),Q.push(t),0);//与堆顶比较判断能否有贡献
			RI d=MxD(S(rt,1),P)>=MxD(S(rt,0),P);Max(S(rt,d),P),Max(S(rt,d^1),P);//优先处理可能带来更优答案的子树
		}
	public:
		I void Build(Point *P) {Build(1,n,rt,P);}
		I void Max(Con Point& P) {Max(rt,P);}
}K;
int main()
{
	RI i;for(F.read(n),F.read(k),i=1;i<=2*k;++i) Q.push(0);//初始放入2k个0
	for(i=1;i<=n;++i) F.read(p[i].x[0]),F.read(p[i].x[1]);K.Build(p);
	for(i=1;i<=n;++i) K.Max(p[i]);return printf("%lld\n",Q.top()),0;//枚举起点更新堆,输出答案
}
posted @ 2020-05-19 14:47  TheLostWeak  阅读(150)  评论(0编辑  收藏  举报