kd树很早就像练一下了但是一直拖到现在。网上找了很多玄学的资料都看不懂,但是直接做了题后马上就懂了。
首先考虑k=1、也就是最远对时的kd树做法。
kd树可以看做每次对k维空间二分。一维就是二分,二维就是想切正方形一样先切成两半,然后再切一刀变成四个小正方形。每次取出一个点,假设目前找到的最大距离为d,就要找有没有和当前点距离大于d的点。如果暴力搜索复杂度是平方,但是如果用kd树,预先存下“当前区间内可能的x/y坐标最大/小数”就可以得出理论上的最远距离,比较一下,如果这个最远距离都小于d那就直接退出。
放到k远对也是一样,只不过之前比较的是最大值,现在比较第k大值,用一个优先队列维护即可。
有一个要注意的地方就是,因为一条线段会被两个点各算一次,如果只维护k个值就会可能只有k/2条线段,解决的方法就是维护2k个值,输出第2k个值即可。
PS kd树的巧妙就在于calc_dis这个函数算的只是理论最大值,因为横坐标差最大值和纵坐标差最大值不一定同时取在一个点上,但这一定是一个上限值,应该算是一个必要但是不充分的条件,但我还是不知道kd树的复杂度怎么证明。。。
PS的PS 最远点对可以用旋转卡壳的方法做,听说这道题也能,就是不断从k-1远对、k-2远对。。向后递推。看起来很神奇的样子。
#include<cmath>
#include<cstdio>
#include<vector>
#include <queue>
#include<cstring>
#include<iomanip>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#define ll long long
#define inf 1000000000
#define mod 1000000007
#define N 200000
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
priority_queue<ll,vector<ll>,greater<ll> > q;
struct kd_tree{int d[2],Mx[2],Mn[2],ls,rs;} tree[N];
int n,k,i,root,Mrk;
int nw[2];
void update(int rt)
{
int l = tree[rt].ls; int r = tree[rt].rs; int i;
fo(i,0,1)
{
if (l > 0) tree[rt].Mx[i] = max(tree[rt].Mx[i],tree[l].Mx[i]),tree[rt].Mn[i] = min(tree[rt].Mn[i],tree[l].Mn[i]);
if (r > 0) tree[rt].Mx[i] = max(tree[rt].Mx[i],tree[r].Mx[i]),tree[rt].Mn[i] = min(tree[rt].Mn[i],tree[r].Mn[i]);
}
}
bool cmp(const kd_tree &x,const kd_tree &y) {return x.d[Mrk]<y.d[Mrk];}
void build(int &rt,int l,int r,int mrk)
{
int mid = (l + r) >> 1; Mrk = mrk; rt = mid;
nth_element(tree+l,tree+mid,tree+r+1,cmp);
tree[rt].Mx[0] = tree[rt].Mn[0] = tree[rt].d[0];
tree[rt].Mx[1] = tree[rt].Mn[1] = tree[rt].d[1];
if (l < mid) build(tree[rt].ls,l,mid-1,mrk^1);
if (r > mid) build(tree[rt].rs,mid+1,r,mrk^1);
update(rt);
}
ll sqr(const int &x) {return (ll)x*x;}
ll calc_dis(int k)
{
if (k == 0) return 0;
return max(sqr(tree[k].Mx[0]-nw[0]),sqr(tree[k].Mn[0]-nw[0]))+
max(sqr(tree[k].Mx[1]-nw[1]),sqr(tree[k].Mn[1]-nw[1]));
}
void query(int k)
{
ll dis = sqr(tree[k].d[0] - nw[0]) + sqr(tree[k].d[1] - nw[1]);
if (dis > q.top()) {q.pop(); q.push(dis);}
ll dis_l = calc_dis(tree[k].ls); ll dis_r = calc_dis(tree[k].rs);
if (dis_l > dis_r)
{
if (dis_l > q.top()) query(tree[k].ls);
if (dis_r > q.top()) query(tree[k].rs);
} else
{
if (dis_r > q.top()) query(tree[k].rs);
if (dis_l > q.top()) query(tree[k].ls);
}
}
int main()
{
scanf("%d%d",&n,&k);
fo(i,1,n) scanf("%d%d",&tree[i].d[0],&tree[i].d[1]);
build(root,1,n,0);
fo(i,1,k*2) q.push(0);
fo(i,1,n)
{
nw[0] = tree[i].d[0]; nw[1] = tree[i].d[1];
query(root);
}
printf("%lld\n",q.top());
return 0;
}