[BZOJ4520][CQOI2016]K远点对

bzoj
luogu

题意

求欧几里德距离第\(k\)远点对。
\(n\le10^5,k\le\min(\frac{n(n-1)}{2},100)\)

sol

直接\(kdt\)了。拿一个小根堆保存最优答案,每次算出的答案和堆顶比较。
欧几里德距离最大的估价
\(\max((x-t[o].x[0])^2,(x-t[o].x[1])^2)+\max((y-t[o].y[0])^2,(y-t[o].y[1])^2)\)

code

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
#define ll long long
#define ls t[o].ch[0]
#define rs t[o].ch[1]
#define cmin(a,b) (a>b?a=b:a)
#define cmax(a,b) (a<b?a=b:a)
const int N = 1e5+5;
int n,k,D,root;
struct node{
    int d[2];
    bool operator < (const node &b) const
        {return d[D]<b.d[D];}
}a[N];
struct kdtree{int d[2],Min[2],Max[2],ch[2];}t[N];
priority_queue<ll,vector<ll>,greater<ll> >Q;
ll sqr(int x){return 1ll*x*x;}
void mt(int x,int y){
    cmin(t[x].Min[0],t[y].Min[0]);cmax(t[x].Max[0],t[y].Max[0]);
    cmin(t[x].Min[1],t[y].Min[1]);cmax(t[x].Max[1],t[y].Max[1]);
}
int build(int l,int r,int d){
    D=d;int o=l+r>>1;
    nth_element(a+l,a+o,a+r+1);
    t[o].d[0]=t[o].Min[0]=t[o].Max[0]=a[o].d[0];
    t[o].d[1]=t[o].Min[1]=t[o].Max[1]=a[o].d[1];
    if (l<o) ls=build(l,o-1,d^1),mt(o,ls);
    if (o<r) rs=build(o+1,r,d^1),mt(o,rs);
    return o;
}
ll dist(int o,int x,int y){
    return max(sqr(x-t[o].Min[0]),sqr(t[o].Max[0]-x))+max(sqr(y-t[o].Min[1]),sqr(t[o].Max[1]-y));
}
void query(int o,int x,int y){
    ll tmp=sqr(x-t[o].d[0])+sqr(y-t[o].d[1]),d[2];
    if (tmp>Q.top()) Q.pop(),Q.push(tmp);
    if (ls) d[0]=dist(ls,x,y);else d[0]=0;
    if (rs) d[1]=dist(rs,x,y);else d[1]=0;
    tmp=d[0]>=d[1];
    if (d[tmp]>Q.top()) query(t[o].ch[tmp],x,y);tmp^=1;
    if (d[tmp]>Q.top()) query(t[o].ch[tmp],x,y);
}
int main(){
    n=gi();k=gi()<<1;
    for (int i=1;i<=k;++i) Q.push(0);
    for (int i=1;i<=n;++i) a[i]=(node){gi(),gi()};
    root=build(1,n,0);
    for (int i=1;i<=n;++i) query(root,a[i].d[0],a[i].d[1]);
    printf("%lld\n",Q.top());return 0;
}

转载于:https://www.cnblogs.com/zhoushuyu/p/9077920.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值