在这片梦想之地,不堪回首的过去像泡沫一样散去,不愿面对的明天也永远不会到来,人们为何选择沉睡?是因为害怕从梦中醒来。|

PassName

园龄:3年1个月粉丝:32关注:16

K-D Tree

什么是 KDT

一种平衡树, 维护 k

维空间中的点的信息.

每个节点表示一个点, 每个子树表示对应的 k
维超长方体. 这样说可能过于抽象, 那就先分析特殊情况, 因为 k=1的时候, 维护的信息是序列上的, 所以这时的 1d Tree 就是普通平衡树.

k=2 的情况, 维护的是平面上的点, 每次选一个点, 将平面分成两半, 左右子树各占存一半的点. 每次选点之前, 首先确定要分割的这个四边形, 将要存入这个子树中的所有点的横纵坐标最大值和最小值都求出来, 这 4 个坐标围起来的四边形就是要分割的四边形.

然后选择这个点, 看这个四边形是横着比较长还是竖着比较长, 也就是说我们要尽量保证这个四边形的长宽差越小越好. 不失一般性, 假设这个四边形是躺着的, 也就是横着比较长. 这时需要把它竖着劈成两半, 将这些点按横坐标排序, 选中间的点作为割点, 以它的横坐标为标准划一根竖直线, 将四边形分成两个部分, 左边的部分就是这个点的左子树; 右边的部分就是右子树.

实现

由于每一个节点的分割方向都可以不同, 所以不能保证中序遍历的点以某种规律单调, 因此没法用基于旋转的平衡树维护, 这时我们就想到了替罪羊树.

因为没法以中序遍历出的序列为重构之后的树的序, 因此没法想一般替罪羊的重构方式一样重构, 需要重新求每一维坐标的极差, 然后以某个维度的坐标为序排序. 所以一次重构相比一般的替罪羊是增加了一个 log的, 但是根据前面链接中文章的证明, 重构加一个 log 并不会影响复杂度. (其实还可以使用神奇的 nth_element() 避免排序, O(n),找到中位数, 使一次重构还是线性的,个人非常喜欢用!).

一般来说, 在大部分需要 kd Tree 的题目中, k=2, 结合具体题目分析

例题S2OJ #1438.K-D Tree

#include<bits/stdc++.h>

#define rint register int
#define endl '\n'
 
using namespace std;

const int N = 2e5 + 5;
const int INf = 0x3f3f3f3f;
const double a = 0.75;

int inline min(int a,int b){
	return a<b?a:b; 
}

int inline max(int a,int b){
	return a>b?a:b;
}

int n,op,a1,a2,b1,b2,fans,root,cur,d[N],lc[N],rc[N],L[N],R[N],D[N],U[N],siz[N],sum[N],g[N],t;
struct node{int x,y,v;}s[N];

bool inline cmp1(int a,int b){ 
    return s[a].x<s[b].x; 
}

bool inline cmp2(int a,int b){
    return s[a].y<s[b].y; 
}

void inline dfs(int x){
    if(!x)
       return;
    dfs(lc[x]);
    g[++t]=x;
    dfs(rc[x]);
    return ;
}

void inline solve(int x){
    siz[x]=siz[lc[x]]+siz[rc[x]]+1;
    sum[x]=sum[lc[x]]+sum[rc[x]]+s[x].v;
    L[x]=R[x]=s[x].x,D[x]=U[x]=s[x].y;
    if(lc[x]){
        L[x]=min(L[x],L[lc[x]]);
	    R[x]=max(R[x],R[lc[x]]);
        D[x]=min(D[x],D[lc[x]]); 
	    U[x]=max(U[x],U[lc[x]]);  	
    }
    if(rc[x]){
        L[x]=min(L[x],L[rc[x]]);
	    R[x]=max(R[x],R[rc[x]]);
        D[x]=min(D[x],D[rc[x]]);
	    U[x]=max(U[x],U[rc[x]]);    	
    }
    return ;
}

int inline build(int l,int r){
    if(l>r) return 0;
    int mid=(l+r)>>1;
    double gj1=0;
	double gj2=0;
	double hk1=0;
	double hk2=0;
	
    for(rint i=l;i<=r;i++){
    	gj1+=s[g[i]].x;
	gj2+=s[g[i]].y;
    } 
	
    gj1/=(r-l+1);
    gj2/=(r-l+1);
    
    for(rint i=l;i<=r;i++){
        hk1+=(gj1-s[g[i]].x)*(gj1-s[g[i]].x),
        hk2+=(gj2-s[g[i]].y)*(gj2-s[g[i]].y);	
    }

    if(hk1>hk2){
    	nth_element(g+l,g+mid,g+r+1,cmp1);
	d[g[mid]]=1;
    }
        
    else{
    	nth_element(g+l,g+mid,g+r+1,cmp2);
	d[g[mid]]=2;
    }
        
    lc[g[mid]]=build(l,mid-1);
    rc[g[mid]]=build(mid+1,r);
    
    solve(g[mid]);
    return g[mid];
}

void inline rebuild(int& x){
    t=0;
    dfs(x);
    x=build(1,t);
    return ;
}

bool inline cxom(int x){ 
    return a*siz[x]<=(double)max(siz[lc[x]],siz[rc[x]]); 
}

void inseroot(int& x, int v) {
    if(!x){x=v;solve(x);return ;}
    if(d[x]==1){
        if(s[v].x<=s[x].x) inseroot(lc[x],v);
        else inseroot(rc[x], v);
    } 
	else{
        if(s[v].y <= s[x].y) inseroot(lc[x],v);
        else inseroot(rc[x],v);
    }
    solve(x);
    if(cxom(x)) rebuild(x);
    return ;
}

int inline query(int x){
    if(!x||a2<L[x]||a1>R[x]||b2<D[x]||b1>U[x]) return 0;
    if(a1<=L[x]&&R[x]<=a2&&b1<=D[x]&&U[x]<=b2) return sum[x];
    int ress=0;
    if(a1<=s[x].x&&s[x].x<=a2&&b1<=s[x].y&&s[x].y<=b2) 
       ress+=s[x].v;
    return query(lc[x])+query(rc[x])+ress;
}

signed main(){
    cin>>n;
    while(~scanf("%lld",&op)){
        if(op==1){
            cur++;
	    cin>>s[cur].x>>s[cur].y>>s[cur].v;
            s[cur].x^=fans,s[cur].y^=fans,s[cur].v^=fans;
            inseroot(root,cur);
        }
        if(op==2){
            cin>>a1>>b1>>a2>>b2;
	    a1^=fans;b1^=fans;a2^=fans;b2^=fans;
	    printf("%lld",fans=query(root));
	    puts("");
	}
        if(op==3){exit(0);}
    }
    return 0;
}

本文作者:PassName

本文链接:https://www.cnblogs.com/spaceswalker/p/16511168.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   PassName  阅读(35)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起