HDU 2966 In case of failure (k-d树)

题意:给出n个点,找出每个点距离他们最近的点的距离的平方。


思路:k-d树的模板题,先将所有点建立好k-d树,然后在依次求一下距离他们最近的点,如果找到的点是他们自己的时候设为距离为无穷大即可。


///k-d树
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
typedef long long ll;
const int maxn = 2 * 1e5 + 10;
const ll INF = 3 * 1e18;
const double eps = 1e-6;
using namespace std;

int k, n, nodecnt, t; ///点的维数,个数
struct point {
    ll data[5];
    int split; ///分裂维
    void input() { for(int i = 0; i < k; i++) scanf("%lld", &data[i]); }
    bool operator < (point p) const {
        int i = split;
        if(data[i] != p.data[i]) return data[i] < p.data[i];
        for(int j = 0; j < k; j++) {
            if(data[j] == p.data[j]) continue;
            return data[j] < p.data[j];
        }
        return false;
    }
} p[maxn], ot[maxn];
struct node {
    int son[2];
    point p;
    int split; ///分裂维
} kd[maxn];

///建立k-d树
void build(int o, int l, int r) {
    if(r < l) return ;
    int nn = r - l + 1, sp;
    double EX[5], EXX[5];
    memset(EX, 0, sizeof EX);     ///E(X)
    memset(EXX, 0, sizeof EXX);   ///E(X^2)
    for(int i = l; i <= r; i++) {
        for(int j = 0; j < k; j++) {
            EX[j] += (double)p[i].data[j] / nn;
            EXX[j] += (double)p[i].data[j] * p[i].data[j] / nn;
        }
    }
    ///D(X) = E(X^2) - E(X)^2,将方差最大的维作为分裂维
    double dx = -1;
    for(int i = 0; i < k; i++) {
        double d = EXX[i] - EX[i] * EX[i];
        if(dx < d) { dx = d; sp = i; }
    }
    kd[o].split = sp;
    for(int i = l; i <= r; i++) {
        p[i].split = sp;
    }
    int mid = (l + r) >> 1;
    ///sort(p + l, p + r + 1);
    nth_element (p + l, p + mid, p + r + 1);  ///O(n)
    for(int i = 0; i < k; i++) {
        kd[o].p.data[i] = p[mid].data[i];
    }
    if(mid - 1 >= l) {
        nodecnt++; kd[o].son[0] = nodecnt;
        build(kd[o].son[0], l, mid - 1);
    }
    if(mid + 1 <= r) {
        nodecnt++; kd[o].son[1] = nodecnt;
        build(kd[o].son[1], mid + 1, r);
    }
}
///两点的距离平方
ll dis(point a, point b) {
    ll d = 0;
    for(int i = 0; i < k; i++) {
        d += (a.data[i] - b.data[i]) * (a.data[i] - b.data[i]);
    }
    return d;
}

///寻找距离点p最近的点
ll mindistance(int o, point po) {
    if(!o) return INF;
    int s = 0;
    ///寻找进入的子树
    if(kd[o].p.data[kd[o].split] != po.data[kd[o].split]) {
        s = kd[o].p.data[kd[o].split] > po.data[kd[o].split] ? 0 : 1;
    } else {
        for(int i = 0; i < k; i++) {
            if(kd[o].p.data[i] == po.data[i]) continue;
            s = kd[o].p.data[i] > po.data[i] ? 0 : 1;
            break;
        }
    }
    ///查询点的位置
    int nxt = kd[o].son[s];
    int other = kd[o].son[s ^ 1];
    ll di = dis(kd[o].p, po), dr = 0;
    if(!di) di = INF;
    ll dl = mindistance(nxt, po);
    if(dl) di = min(di, dl);
    if(!di) di = INF;
    double g = sqrt(di);
    
    ///是否要进入另一棵子树
    ll f = s ? -1 : 1;
    ll lo = kd[o].p.data[kd[o].split]; ///当前节点分裂维的坐标
    double lp = po.data[kd[o].split] + f * sqrt(di);
    if(s == 0 && lp + eps > lo) dr = mindistance(other, po);
    if(s == 1 && lp - eps < lo) dr = mindistance(other, po);
    if(di && dr) di = min(di, dr);
    else if(!di) di = dr;
    if(!di) di = INF;
    return di;
}

int main() {
    k = 2;
    scanf("%d", &t);
    while(t--) {
        scanf("%d", &n);  nodecnt = 1;
        for(int i = 0; i < maxn; i++) {
            kd[i].son[0] = kd[i].son[1] = 0;
        }
        for(int i = 0; i < n; i++) {
            p[i].input();
            ot[i].data[0] = p[i].data[0];
            ot[i].data[1] = p[i].data[1];
        }
        build(1, 0, n - 1);
        for(int i = 0; i < n; i++) {
            printf("%lld\n", mindistance(1, ot[i]));
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值