以下是一些奇怪的链接有兴趣的可以看看:

https://blog.sengxian.com/algorithms/k-dimensional-tree

http://zgjkt.blog.uoj.ac/blog/1693

https://en.wikipedia.org/wiki/K-d_tree

http://homes.ieu.edu.tr/hakcan/projects/kdtree/kdTree.html

k-d tree就是一个把一个平面(或超平面)划分的东西…

例如一维情况就是在划分一个数轴,这时就成了我们喜闻乐见的二叉搜索树。

二维情况

用横线和竖线,每次把一个矩形瓜分成两半。

还有三维的,四维的,以此类推…… k维的就叫 k-d tree。

这篇文章就讲一下二维的k-d tree。

每一次我们对于k-d tree上的一个点,我们要沿着某一个轴切开,那么这个轴怎么确定呢?一个切x轴的点的孩子就切y轴,切y轴的点的孩子就切x轴,轮流切分,这样做比较优秀,又比较好写。

例如这个点是切x轴的,为了查询比较和谐,那么它的左子树所有点都要x比它小,右子树所有点都要x比它大(当然也可以是等于),这点与二叉搜索树是一致的)。

①建树

比如我现在有n个点,现在要建成一棵美观的k-d tree,怎么建呢?

在bst中我们一般是选比较中间的一个数,然后递归下去建树。

k-d tree中也是类似的,不过我们要确保当前节点的左子树的点当前维度都比它小,右子树的点当前维度都比它大。我们可以每一次在当前层sort一下当前维度,然后选出中位数?

注意到我们只要让左边比较小,右边比较大,中间比较中间就行了,sort一下显然有点浪费,到了下一维度又要重新sort。这里我们使用stl中的nth_element函数,该函数内部用了quick_select算法(就是快速排序的前半部分)来干这件事,复杂度O(n)。

void nth_element(first,nth,last[,cmp]) (比较函数可选)

对[first,last)的元素重新布置,使得nth的元素在sort之后应该在的位置上,而前面的元素都不大于它,后面的元素都不小于它

这样我们就可以建出一棵比较平衡的k-d tree啦。参考线段树建树的复杂度证明很容易发现这玩意儿也是O(nlogn)的。

②插入

额似乎这玩意儿没必要讲,也就是和当前点的当前维度比较一下然后插入就行。

对于一棵比较平衡的k-d tree,插入显然是O(logn)的。

③询问

首先我们要解决的是…k-d tree有卵用?

显然k-d tree可以被当作是一个比较厉害的k维(本文中是二维)线段树!在这上面可以打tag,可以搞询问(划分矩形的时候注意一下),甚至还可以搞segment tree beats(参见吉司机的集训队论文)

但是二维情况下k-d tree当作线段树用处不大…因为前有二维树状数组/二维线段树,后有cdq分治、整体二分啥的…除非出题人丧心病狂又卡空间又要在线

正常的k-d tree是用来询问最近点的利器!

比如有一道这样的题(bzoj2648),要求每次询问离一个给定点最近(曼哈顿距离)的黑点和加入一个黑点。

真是良心出题人!

查找时我们可以这么做,先把ans设成inf,然后我们从根结点开始递归查询。

对于k-d tree上的一个节点,先用这个点更新答案。然后对于它的两个孩子,优先选择离当前点近的一个矩形(注意这个值显然不一定能取到)递归更新答案,然后更新完答案如果到另一个儿子的那个矩形距离比答案小就也用另一个儿子递归更新答案。

对于一棵比较平衡的k-d tree,这样查询的复杂度最坏是O(sqrt(n))的,一般是O(log(n))的。

(这个“一般”的性质就跟spfa所谓的O(ke)差不多)

可以注意到这里的“近”“距离”对于欧几里得距离与曼哈顿距离都适用。

至于k近点对也比较简单,我们维护一个大根堆,每次比较堆顶啥的就行。

显然这样的复杂度比刚才要多一个klogk。

需要注意的是,你可能需要把k*=2(因为一个点对会算两次)

④一些小问题

有没有注意到刚才的复杂度上有若干个类似“比较平衡”的字眼?

是的,k-d tree的性质就与一棵没有旋转的二叉查找树差不多。

如果建完树不再插入,这时k-d tree是十分平衡的,可以保证查询的一切复杂度。

如果建完树还插入或者干脆不建树直接插入,这时k-d tree对于随机数据仍然是“十分平衡”的,而随便构造一个数据(例如1,1 2,2 3,3...这样的数据)就可以成功地卡飞k-d tree。

所以如果又有插入又有询问,你只能假装数据是随机的...

代码(常数非常大,有一些题目可能无法通过...原因玄学)

bzoj2716(权限,时限80s才勉强艹过去)

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;
#define gc getchar()
int g_i()
{
    ; ; char s;
    ')) ;
    ; ';
    +s-';
    if(fu) return -tmp; else return tmp;
}
#define gi g_i()
#define pob
#define pc(x) (putchar(x))
struct foce {~foce() {pob; fflush(stdout);}} _foce;
inline void pstr(char* p) {while(*p)pc(*(p++));}
];}
inline void pi(int x)
{
    ) {pc(); return;}
    ) {pc('-'); x=-x;}
    char *s=ib::b;
    , x/=;
    );
}
struct pnt
{
    int _x,_y;
    pnt() {}
    pnt(int x,int y) {_x=x; _y=y;}
};
)?x:-x;}
#define SZ 1234567
int dis(pnt a,pnt b) {return Abs(a._x-b._x)+Abs(a._y-b._y);}
//kdtree
bool dim_;
bool cmp(pnt a,pnt b)
{
    if(!dim_) return a._x<b._x||(a._x==b._x&&a._y<b._y);
    else return a._y<b._y||(a._y==b._y&&a._x<b._x);
}
,ch[SZ][],inf=;
pnt pp[SZ],lu[SZ],rd[SZ];
inline int min_(int a,int b)
{
    return (a<=b)?a:b;
}
inline int min_(int a,int b,int c)
{
    int ans=a;
    if(b<ans) ans=b;
    if(c<ans) ans=c;
    return ans;
}
inline int max_(int a,int b,int c)
{
    int ans=a;
    if(b>ans) ans=b;
    if(c>ans) ans=c;
    return ans;
}
void upd(int x)
{
    lu[x]._x=min_(lu[ch[x][]]._x,lu[ch[x][]]._x,lu[x]._x);
    lu[x]._y=min_(lu[ch[x][]]._y,lu[ch[x][]]._y,lu[x]._y);
    rd[x]._x=max_(rd[ch[x][]]._x,rd[ch[x][]]._x,rd[x]._x);
    rd[x]._y=max_(rd[ch[x][]]._y,rd[ch[x][]]._y,rd[x]._y);
}
int dis(int x,pnt p)
{
    ;
    if(p._x<lu[x]._x) ans+=lu[x]._x-p._x;
    else if(p._x>rd[x]._x) ans+=p._x-rd[x]._x;
    if(p._y<lu[x]._y) ans+=lu[x]._y-p._y;
    else ans+=p._y-rd[x]._y;
    return ans;
}
void init()
{
    rot=M=;
    lu[]._x=inf; lu[]._y=inf;
    rd[]._x=-inf; rd[]._y=-inf;
}
pnt ps[SZ];
)
{
    ;
    ; dim_=d;
    nth_element(ps+l,ps+m,ps+r,cmp);
    pp[c]=lu[c]=rd[c]=ps[m];
    ch[c][]=build(l,m,!d);
    ch[c][]=build(m+,r,!d);
    upd(c); return c;
}
int ans;
void query(int x,pnt p)
{
    if(!x) return;
    ans=min_(ans,dis(p,pp[x]));
    ]={dis(ch[x][],p),dis(ch[x][],p)};
    ]>dc[];
    query(ch[x][d],p);
    if(dc[!d]<ans) query(ch[x][!d],p);
}
void ins(int& x,pnt p)
{
    if(!x) {x=++M; pp[x]=lu[x]=rd[x]=p; return;}
    int d=!cmp(p,pp[x]); dim_=!dim_;
    ins(ch[x][d],p); upd(x);
}
int n,m;
int main()
{
    init(); n=gi, m=gi;
    ;i<n;i++) ps[i]._x=gi, ps[i]._y=gi;
    rot=build(,n);
    while(m--)
    {
        int t=gi, x=gi, y=gi;
        ) {dim_=; ins(rot,pnt(x,y));}
        )
        {
            ans=inf; query(rot,pnt(x,y));
            pi(ans); pc();
        }
    }
    pob;
}

bzoj4520

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long ll;
#define gc getchar()
int g_i()
{
    ; ; char s;
    ')) ;
    ; ';
    +s-';
    if(fu) return -tmp; else return tmp;
}
#define gi g_i()
#define pob
#define pc(x) (putchar(x))
struct foce {~foce() {pob; fflush(stdout);}} _foce;
inline void pstr(char* p) {while(*p)pc(*(p++));}
];}
inline void pi(int x)
{
    ) {pc(); return;}
    ) {pc('-'); x=-x;}
    char *s=ib::b;
    , x/=;
    );
}
struct pnt
{
    int _x,_y;
    pnt() {}
    pnt(int x,int y) {_x=x; _y=y;}
};
)?x:-x;}
ll pf(ll x) {return x*x;}
#define SZ 1234567
ll dis(pnt a,pnt b) {return pf(a._x-b._x)+pf(a._y-b._y);}
//kdtree
bool dim_;
bool cmp(pnt a,pnt b)
{
    if(!dim_) return a._x<b._x||(a._x==b._x&&a._y<b._y);
    else return a._y<b._y||(a._y==b._y&&a._x<b._x);
}
,ch[SZ][];
ll inf=;
ll inf_ll=100000000000000000LL;
pnt pp[SZ],lu[SZ],rd[SZ];
inline int min_(int a,int b)
{
    return (a<=b)?a:b;
}
inline int min_(int a,int b,int c)
{
    int ans=a;
    if(b<ans) ans=b;
    if(c<ans) ans=c;
    return ans;
}
inline int max_(int a,int b,int c)
{
    int ans=a;
    if(b>ans) ans=b;
    if(c>ans) ans=c;
    return ans;
}
void upd(int x)
{
    lu[x]._x=min_(lu[ch[x][]]._x,lu[ch[x][]]._x,lu[x]._x);
    lu[x]._y=min_(lu[ch[x][]]._y,lu[ch[x][]]._y,lu[x]._y);
    rd[x]._x=max_(rd[ch[x][]]._x,rd[ch[x][]]._x,rd[x]._x);
    rd[x]._y=max_(rd[ch[x][]]._y,rd[ch[x][]]._y,rd[x]._y);
}
ll dis(int x,pnt p)
{
    ll ans=;
    ans=max(ans,dis(rd[x],p));
    ans=max(ans,dis(lu[x],p));
    ans=max(ans,dis(pnt(rd[x]._x,lu[x]._y),p));
    ans=max(ans,dis(pnt(lu[x]._x,rd[x]._y),p));
    return ans;
}
void init()
{
    rot=M=;
    lu[]._x=inf; lu[]._y=inf;
    rd[]._x=-inf; rd[]._y=-inf;
}
pnt ps[SZ];
)
{
    ;
    ; dim_=d;
    nth_element(ps+l,ps+m,ps++r,cmp);
    pp[c]=lu[c]=rd[c]=ps[m];
    ch[c][]=build(l,m-,!d);
    ch[c][]=build(m+,r,!d);
    upd(c); return c;
}
typedef priority_queue<ll,vector<ll>,greater<ll> >sheap;
sheap pq;
void query(int x,pnt p)
{
    if(!x) return;
    ll d1=dis(p,pp[x]);
    if(d1>=pq.top()) pq.pop(), pq.push(d1);
    ll dc[]={dis(ch[x][],p),dis(ch[x][],p)};
    ]<dc[];
    query(ch[x][d],p);
    if(dc[!d]>=pq.top()) query(ch[x][!d],p);
}
int n,m;
int main()
{
    init();
    n=gi, m=gi;
    ;i<=n;i++) ps[i]._x=gi, ps[i]._y=gi;
    rot=build(,n);
    ;i<=m*;i++) pq.push(-inf_ll);
    ;i<=n;i++) query(rot,ps[i]);
    printf("%lld\n",pq.top());
}

P.S. 常数比较优秀的代码在 https://blog.sengxian.com/algorithms/k-dimensional-tree 这里有...指针版

k-d tree 学习笔记的更多相关文章

  1. kd tree学习笔记 (最近邻域查询)

    https://zhuanlan.zhihu.com/p/22557068 http://blog.csdn.net/zhjchengfeng5/article/details/7855241 KD树 ...

  2. bzoj 1598: [Usaco2008 Mar]牛跑步 [k短路 A*] [学习笔记]

    1598: [Usaco2008 Mar]牛跑步 题意:k短路 ~~貌似A*的题目除了x数码就是k短路~~ \[ f(x) = g(x) + h(x) \] \(g(x)\)为到达当前状态实际代价,\ ...

  3. Ext.Net学习笔记22:Ext.Net Tree 用法详解

    Ext.Net学习笔记22:Ext.Net Tree 用法详解 上面的图片是一个简单的树,使用Ext.Net来创建这样的树结构非常简单,代码如下: <ext:TreePanel runat=&q ...

  4. MySQL学习笔记一

    MySQL 学习笔记 一 一.数据库简单介绍 1. 按照数据库的发展时间顺序,主要出现了以下类型数据库系统: Ø 网状型数据库 Ø 层次型数据库 Ø 关系型数据库 Ø 面向对象数据库 上面4中数据库系 ...

  5. shell学习笔记

    shell学习笔记 .查看/etc/shells,看看有几个可用的Shell . 曾经用过的命令存在.bash_history中,但是~/.bash_history记录的是前一次登录前记录的所有指令, ...

  6. OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波

    http://blog.csdn.net/chenyusiyuan/article/details/8710462 OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波 201 ...

  7. Matplotlib学习笔记(二)

    原  Matplotlib学习笔记 参考:Python数据科学入门教程 Python3.6.1 jupyter notebook .caret, .dropup > .btn > .car ...

  8. Matplotlib学习笔记(一)

    原   matplotlib学习笔记 参考:Python数据科学入门教程 Python3.6.1 jupyter notebook .caret, .dropup > .btn > .ca ...

  9. 消息队列——RabbitMQ学习笔记

    消息队列--RabbitMQ学习笔记 1. 写在前面 昨天简单学习了一个消息队列项目--RabbitMQ,今天趁热打铁,将学到的东西记录下来. 学习的资料主要是官网给出的6个基本的消息发送/接收模型, ...

随机推荐

  1. UITextView 输入字数限制

    本文介绍了UITextView对中英文还有iOS自带表情输入的字数限制,由于中文输入会有联想导致字数限制不准确所以苦恼好久,所以参考一些大神的博客终于搞定,欢迎大家参考和指正. 对于限制UITextV ...

  2. CustomEvent自定义事件

    javascript与HTML之间的交互是通过事件来实现的.事件,就是文档或浏览器窗口发生的一些特定的交互瞬间.通常大家都会认为事件是在用户与浏览器进行交互的时候触发的,其实通过javascript我 ...

  3. 论文阅读(Xiang Bai——【CVPR2016】Multi-Oriented Text Detection with Fully Convolutional Networks)

    Xiang Bai--[CVPR2016]Multi-Oriented Text Detection with Fully Convolutional Networks 目录 作者和相关链接 方法概括 ...

  4. linux运维自动化shell脚本小工具

    linux运维shell 脚本小工具,如要分享此文章,请注明文章出处,以下脚本仅供参考,若放置在服务器上出错,后果请自负 1.检测cpu剩余百分比 #!/bin/bash #Inspect CPU # ...

  5. PIC XC8 EEPROM操作

    要做一个报警功能的东东,要求可以通过遥控来改变遥控内容.由于对系统的稳定性要求很高,所以用了看门狗. 可是看门狗复位会引起所有寄存器重置,恢复到默认状态.遥控要改变的内容也被复位了,所以只能借助EEP ...

  6. JS原型和继承

    //所有的函数都有一个prototype属性 function aa() { } console.info(aa.prototype); //这个prototype属性引用了一个对象,即原型,初始化时 ...

  7. C# winform窗体设计-通过条件查询数据

    在winform 数据库设计中,有时候需要通过条件查询对应的数据,并且将数据显示在文本框(or 富文本框)中,下面,小编将讲述通过一个条件: 首先,我们需要对数据库建立连接,并且执行数据库命令,在此之 ...

  8. 纪念逝去的岁月——C/C++二分查找

    代码 #include <stdio.h> int binarySearch(int iList[], int iNum, int iX, int * pPos) { if(NULL == ...

  9. WCF服务接口多,客户端在引用时出错!报WCF The maximum nametable character count quota (16384) has been exceeded while reading XML data错误

    WCF服务接口多,客户端在引用时出错!报WCF The maximum nametable character count quota (16384) has been exceeded while ...

  10. iOS使用textfield注意的细节

    一般做登录界面或者要填写表之类的页面会经常使用到textfield.使用很简单,但是其实他有很多小的处理细节,这回让你显得有经验,交互性很好.在这里呢,我就直接拿stroyboard中的截图来说. c ...