KDtree全纪录

KDtree是一种很优秀的暴力(实质上也是线段树)
可以解决:n维k远点对问题
有些需要CDQ分治的难题,用KDtree就可以解决了(思维难度较小)

经典例题:天使玩偶
KDtree
CDQ分治
实测KDtree较快,然而dalao好像都不喜欢用这种暴力做法,都是推荐CDQ
然而蒟蒻就有蒟蒻的方法,暴力什么的先稳住再说

找到了KDtree的复杂度分析:

构建: O(log2n) O ( l o g 2 n )
O(logn) 插 入 : O ( l o g n )
O(logn) 删 除 : O ( l o g n )
O(n11/k+m) 查 询 : O ( n 1 − 1 / k + m ) m—每次要搜索的最近点个数

这里有KDtree的详解

下面的代码普适性更高,可以扩展到任意维度
(给出的是最近点对的代码,如果想求k远点对,我们只要开一个优先队列记录一下就好了,只有队列不满或者当前dis小于队首元素我们才进行这个方向的检索,值得注意的是:priority_queue是大根堆

const int INF=0x33333333;
const int N=1000010;
const int o=2;
struct node{
    int d[o];     //根结点坐标 
    int mn[o];
    int mx[o];
    int l,r,dis; 
    bool operator <(const node &a) const{
        return dis<a.dis;
    }
}t[N];
int x[o],n,m,root,ans;

void update(int x) {
    int lc=t[x].l;
    int rc=t[x].r;
    if (lc) {
        for (int i=0;i<o;i++) {
            t[x].mn[i]=min(t[x].mn[i],t[lc].mn[i]);
            t[x].mx[i]=max(t[x].mx[i],t[lc].mx[i]);
        }
    }
    if (rc) {
        for (int i=0;i<o;i++) {
            t[x].mn[i]=min(t[x].mn[i],t[rc].mn[i]);
            t[x].mx[i]=max(t[x].mx[i],t[rc].mx[i]);
        }
    }
}

int build(int l,int r,int D) {
    int mid=(l+r)>>1;
    for (int i=l;i<=r;i++) t[i].dis=t[i].d[D];   //比较标志 
    nth_element(t+l,t+mid+1,t+r+1);              //l~r中排名为mid的元素放在mid位置上
    for (int i=0;i<o;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)%o);
    if (r!=mid) t[mid].r=build(mid+1,r,(D+1)%o);
    update(mid);
    return mid; 
}

//t[n].mn[i]=t[n].mx[i]=t[n].d[i]=x[i];
void insert(int p) {                              //插入一个单点代表的区间 
    int D,now;
    D=0,now=root;
    while (1) {
        for (int i=0;i<o;i++) {
            t[now].mn[i]=min(t[now].mn[i],t[p].mn[i]);
            t[now].mx[i]=max(t[now].mx[i],t[p].mx[i]);
        }
        if (t[p].d[D]>=t[now].d[D]) {
            if (!t[now].r) {
                t[now].r=p;
                return;
            }
            else now=t[now].r;
        }
        else {
            if (!t[now].l) {
                t[now].l=p;
                return;
            }
            else now=t[now].l;
        }
        D=(D+1)%o;
    }
}

int dis(int p) {
    int d=0;
    for (int i=0;i<o;i++) {
        if (x[i]<t[p].mn[i]) d+=(t[p].mn[i]-x[i]);
        if (x[i]>t[p].mx[i]) d+=(x[i]-t[p].mx[i]);
    }
    return d;
}

void ask(int now) {
    int d0=0,dl,dr;
    for (int i=0;i<o;i++) d0+=abs(t[now].d[i]-x[i]);
    ans=min(ans,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<ans) ask(t[now].l);
        if (dr<ans) ask(t[now].r);
    }
    else {
        if (dr<ans) ask(t[now].r);
        if (dl<ans) ask(t[now].l);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值