题意
给了n个平面上的点,求欧式距离下第k远点对
数据范围
1 ≤ n ≤ 1 e 5 , 1 ≤ k ≤ 100 , 点 的 坐 标 ≤ 2 31 1\le n\le1e5,1\le k\le100,点的坐标\le 2^{31} 1≤n≤1e5,1≤k≤100,点的坐标≤231
首先kd-tree本质也只是一棵BST,然后我构建这棵树的方式是维度轮换,具体来讲就是第一层用一个维度排序,第二层用另一个维度排序,每一层都和上一层的维度不同。
然后就是kd-tree里的每个节点都有实际含义,和平衡树很像(本质也是BST)。
在这题里,首先前k大的距离可以用堆存。
然后查询距离用kd-tree
首先需要把kd-tree建出来
struct node{
int x[2];
}a[maxn];
int opt;//轮换标记
bool cmp(node a,node b){
return a.x[opt]<b.x[opt];
}
void pushup(int x){
for(int i=0;i<2;i++){mx[x][i]=mn[x][i]=d[x].x[i];}//mn,mx就是当前区间的最小,最大坐标
if(lc){for(int i=0;i<2;i++){mx[x][i]=max(mx[x][i],mx[lc][i]);mn[x][i]=min(mn[x][i],mn[lc][i]);}}
if(rc){for(int i=0;i<2;i++){mx[x][i]=max(mx[x][i],mx[rc][i]);mn[x][i]=min(mn[x][i],mn[rc][i]);}}
}
void build(int &x,int L,int R){
if(L>R)return ;//这里L==R是可以的
x=++cnt;
opt=opt^1;int mid=L+R>>1;
nth_element(a+L,a+mid,a+R+1,cmp);d[x]=a[mid];//建Kd-tree就是在当前区间中选出一个最中间的
build(lc,L,mid-1);build(rc,mid+1,R);
pushup(x);
}
查询的时候实际上是给了一个坐标,然后在kd-tree中查有没有哪些距离比堆中最小距离(当前的第k大)更大的,有就替换
inline ll pf(int x){
return 1ll*x*x;
}
inline ll f(node a,node b){return pf(a.x[0]-b.x[0])+pf(a.x[1]-b.x[1]);}
inline ll g(node a,int b){return max(pf(a.x[0]-mx[b][0]),pf(a.x[0]-mn[b][0]))+max(pf(a.x[1]-mx[b][1]),pf(a.x[1]-mn[b][1]));}
inline void query(int x,node y){
ll dl=-inf,dr=-inf;
if(lc) dl=g(y,lc);//g表示一个坐标和一个节点的最大代价可能是多少
if(rc) dr=g(y,rc);
ll di=f(y,d[x]);//f表示具体的两个节点之间的举离
if(-q.top()<di){q.pop();q.push(-di);}
if(dl>dr){if(-q.top()<dl)query(lc,y);if(-q.top()<dr)query(rc,y);}//这里是剪枝如果当前第k大的距离大于子树中最大可能出现的距离,就不继续做了
else {if(-q.top()<dr)query(rc,y);if(-q.top()<dl)query(lc,y);}
}
在查询中可以看出,kd-tree本质上类似一种剪枝。复杂度并不是非常有保证。
总代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5,inf=1<<30;
inline int read(){
char c=getchar();int t=0,f=1;
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
return t*f;
}
int n,k;
struct node{
int x[2];
}a[maxn];
int opt;
bool cmp(node a,node b){
return a.x[opt]<b.x[opt];
}
#define lc l[x]
#define rc r[x]
priority_queue<ll> q;
inline ll pf(int x){
return 1ll*x*x;
}
struct tree{
int mx[maxn<<1][2],mn[maxn<<1][2],l[maxn<<1],r[maxn<<1],cnt;
node d[maxn<<1];
void pushup(int x){
for(int i=0;i<2;i++){mx[x][i]=mn[x][i]=d[x].x[i];}
if(lc){for(int i=0;i<2;i++){mx[x][i]=max(mx[x][i],mx[lc][i]);mn[x][i]=min(mn[x][i],mn[lc][i]);}}
if(rc){for(int i=0;i<2;i++){mx[x][i]=max(mx[x][i],mx[rc][i]);mn[x][i]=min(mn[x][i],mn[rc][i]);}}
}
void build(int &x,int L,int R){
if(L>R)return ;x=++cnt;
opt=opt^1;int mid=L+R>>1;
nth_element(a+L,a+mid,a+R+1,cmp);d[x]=a[mid];
build(lc,L,mid-1);build(rc,mid+1,R);
pushup(x);
}
inline ll f(node a,node b){return pf(a.x[0]-b.x[0])+pf(a.x[1]-b.x[1]);}
inline ll g(node a,int b){return max(pf(a.x[0]-mx[b][0]),pf(a.x[0]-mn[b][0]))+max(pf(a.x[1]-mx[b][1]),pf(a.x[1]-mn[b][1]));}
inline void query(int x,node y){
ll dl=-inf,dr=-inf;
if(lc) dl=g(y,lc);
if(rc) dr=g(y,rc);
ll di=f(y,d[x]);
if(-q.top()<di){q.pop();q.push(-di);}
if(dl>dr){if(-q.top()<dl)query(lc,y);if(-q.top()<dr)query(rc,y);}
else {if(-q.top()<dr)query(rc,y);if(-q.top()<dl)query(lc,y);}
}
}t;
int main(){
//freopen("luogu4357.in","r",stdin);
//freopen("luogu4357.out","w",stdout);
n=read(),k=read();
for(int i=1;i<=n;i++){
a[i].x[0]=read();a[i].x[1]=read();
}
for(int i=1;i<=2*k;i++){
q.push(0);
}
t.build(t.l[0],1,n);
for(int i=1;i<=n;i++)t.query(1,a[i]);
printf("%lld\n",-q.top());
return 0;
}