[BZOJ3053]The Closest M Points(KD-tree+堆)

题目:

我是超链接

题解:

也是一道裸题嘛,只有ask的部分要好好想,到这个部分至少要花费的距离就是出界的范围
这里可以使用大根堆,每次和堆顶比较考虑要不要加进去,比小根堆节省时间空间哦
最后倒着输出就好啦
然后我这个zz调了1h30min+的程序,竟是因为没有清零!!!!!

代码:

#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const double INF=1e18;
struct hh{int d[5],l,r,mn[5],mx[5];}t[50005],y,ans[20];
struct dui{hh di;double jl;bool operator <(const dui&a) const {return jl<a.jl;}};
int k,cmpd,num,m,root;
priority_queue<dui>q;
int cmp(const hh &a,const hh &b)
{
    if (a.d[cmpd]!=b.d[cmpd]) return a.d[cmpd]<b.d[cmpd];
    return a.d[(cmpd+1)%k]<b.d[(cmpd+1)%k];
}
void updata(int now)
{
    int lc=t[now].l,rc=t[now].r;
    if (lc)
    {
        for (int i=0;i<k;i++)
        {
            t[now].mn[i]=min(t[now].mn[i],t[lc].mn[i]);
            t[now].mx[i]=max(t[now].mx[i],t[lc].mx[i]);
        }
    }
    if (rc)
    {
        for (int i=0;i<k;i++)
        {
            t[now].mn[i]=min(t[now].mn[i],t[rc].mn[i]);
            t[now].mx[i]=max(t[now].mx[i],t[rc].mx[i]);
        }
    }
}
int build(int l,int r,int D)
{
    cmpd=D;
    int mid=(l+r)>>1;
    nth_element(t+l+1,t+mid+1,t+r+1,cmp);
    for (int i=0;i<k;i++)
      t[mid].mn[i]=t[mid].mx[i]=t[mid].d[i];
    if (l!=mid) t[mid].l=build(l,mid-1,(D+1)%k);
    if (r!=mid) t[mid].r=build(mid+1,r,(D+1)%k);
    updata(mid); return mid;
}
double dis(int now)
{
    double d=0; 
    for (int i=0;i<k;i++)
    {
        if (y.d[i]<t[now].mn[i]) d+=(double)((y.d[i]-t[now].mn[i])*(y.d[i]-t[now].mn[i]));
        if (y.d[i]>t[now].mx[i]) d+=(double)((y.d[i]-t[now].mx[i])*(y.d[i]-t[now].mx[i]));
    }
    return d;
}
void ask(int now)
{
    double d0=0,dl,dr; 
    for (int i=0;i<k;i++) 
      d0+=(double)((y.d[i]-t[now].d[i])*(y.d[i]-t[now].d[i]));
    if (num<m) num++,q.push((dui){t[now],d0});
    else if (q.top().jl>d0) q.pop(),q.push((dui){t[now],d0});
    if (t[now].l) dl=dis(t[now].l);else dl=INF;
    if (t[now].r) dr=dis(t[now].r);else dr=INF; 
    if (dl<dr)
    {
        if (dl!=INF && (dl<q.top().jl || num<m)) ask(t[now].l);
        if (dr!=INF && (dr<q.top().jl || num<m)) ask(t[now].r);
    }
    else 
    {
        if (dr!=INF && (dr<q.top().jl || num<m)) ask(t[now].r);
        if (dl!=INF && (dl<q.top().jl || num<m)) ask(t[now].l);
    }
}
int main()
{
    int n,i,j,T;
    while (~scanf("%d%d",&n,&k))
    {
    memset(t,0,sizeof(t));
    for (i=1;i<=n;i++)
      for (j=0;j<k;j++)
        scanf("%d",&t[i].d[j]);
    root=build(1,n,0);
    scanf("%d",&T);
    while (T--)
    {
        for (i=0;i<k;i++) scanf("%d",&y.d[i]);
        scanf("%d",&m);num=0;
        ask(root);
        printf("the closest %d points are:\n",m);
        for (i=1;i<=m;i++) {ans[i]=q.top().di; q.pop();}
        for (i=m;i>=1;i--)
        {
            for (j=0;j<k-1;j++) printf("%d ",ans[i].d[j]);
            printf("%d\n",ans[i].d[k-1]);
        }
    }   
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值