[BZOJ4520][CQOI2016] K远点对 - KD-tree

本文介绍了解决Cqoi2016竞赛中“K远点对”问题的方法,该问题要求找出平面上N个点中第K远的点对。通过使用kd-tree算法和优先队列,文章详细阐述了如何高效地解决此问题,并给出了具体的实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

4520: [Cqoi2016]K远点对

Time Limit: 30 Sec   Memory Limit: 512 MB
Submit: 563   Solved: 295
[ 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]


首先我们就会观察到题目要求的k很小,加上是与空间距离有关的,很容易想到kd-tree这种算法。

我们可以考虑先枚举每个点,找到距离最远的另一些点,使得它们比目前优先队列中的最近的一些点对更远。

因为是枚举所以会算两次,k就变成了2k。

这样我们就得到了最远的2k组点对(次序不同算两组),因此最近的一组点对就是答案。

时间复杂度O(nklogk+nsqrt(n))

#include"bits/stdc++.h"
using namespace std;
  
typedef long long ll;
const int L=3000005;
char _buff[L]; int _pos=-1;
void ReadIn(){fread(_buff,L,sizeof(char),stdin);}
#define fge _buff[++_pos]
inline int read(){
    int x=0,f=1; char ch=fge;
    while(ch>'9'||ch<'0')
    {if(ch=='-')f=-1;ch=fge;}
    while(ch<='9'&&ch>='0')
        x=x*10+ch-'0',ch=fge;
    return x*f;
}
  
const int N=100005;
  
struct node{
    int x[2],y[2],d[2];
    void init(){
        x[0]=x[1]=d[0]=read();
        y[0]=y[1]=d[1]=read();
    }
} p[N];
int par,n,k;
bool comp(const int&a,const int&b)
{return p[a].d[par]<p[b].d[par];}
struct KDtree{
    priority_queue <ll,vector<ll>,greater<ll> > q;
    int c[N][2],na[N],rt,nx,ny;KDtree(){rt=n=0;}
    inline void makemx(int&x,int y){x=max(x,y);}
    inline void makemn(int&x,int y){x=min(x,y);}
    inline void update(int x,int y){
        makemn(p[x].x[0],p[y].x[0]);
        makemx(p[x].x[1],p[y].x[1]);
        makemn(p[x].y[0],p[y].y[0]);
        makemx(p[x].y[1],p[y].y[1]);
    }
    void build(int&k,int l,int r,int bas){
        int mid=(l+r)>>1;par=bas;
        nth_element(na+l,na+mid,na+r+1,comp);k=na[mid];
        if(l<mid){
            build(c[k][0],l,mid-1,bas^1);
            update(k,c[k][0]);
        }
        if(r>mid){
            build(c[k][1],mid+1,r,bas^1);
            update(k,c[k][1]);
        }
    }
    void pre(){
        n=read(),k=read();int i;
        for(i=1;i<=n;i++)na[i]=i,p[i].init();
        for(i=1;i<=2*k;i++)q.push(0);
        build(rt,1,n,0);
    }
    inline ll sqr (int x){return x*(ll)x;}
    inline ll calc (int x){
        ll sqrx=max(sqr(p[x].x[0]-nx),sqr(p[x].x[1]-nx));
        ll sqry=max(sqr(p[x].y[0]-ny),sqr(p[x].y[1]-ny));
        return sqrx + sqry;
    }
    void ask(int k){
        if(k==0)return ;
        ll distl=calc(c[k][0]),distr=calc(c[k][1]);
        ll distn=sqr(p[k].d[0]-nx)+sqr(p[k].d[1]-ny);
        if(distn>q.top())q.pop(),q.push(distn);
        if(distl>distr){
            if(distl>q.top()) ask(c[k][0]);
            if(distr>q.top()) ask(c[k][1]);
        } else {
            if(distr>q.top()) ask(c[k][1]);
            if(distl>q.top()) ask(c[k][0]);
        }
    }
    void work(){
        for(int i=1;i<=n;i++){
            nx=p[i].d[0];
            ny=p[i].d[1];
            ask(rt);
        }
    }
} t;
  
int main(){
    ReadIn();t.pre();t.work();
    printf("%lld\n",t.q.top());
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值