这天,SJY显得无聊。在家自己玩。在一个棋盘上,有N个黑色棋子。他每次要么放到棋盘上一个黑色棋子,要么放上一个白色棋子,如果是白色棋子,他会找出距离这个白色棋子最近的黑色棋子。此处的距离是 曼哈顿距离 即(|x1-x2|+|y1-y2|) 。现在给出N<=500000个初始棋子。和M<=500000个操作。对于每个白色棋子,输出距离这个白色棋子最近的黑色棋子的距离。同一个格子可能有多个棋子。
第一行两个整数N和M;
接下来N行,每行为一个点的坐标;
接下来M行,每行3个数t,x,y;
如果t=1,那么放下一个黑色棋子;
如果t=2,那么放下一个白色棋子;
对于每个T=2 输出一个最小距离
2 3 1 1 2 3 2 1 2 1 3 3 2 4 2
1 2
这天,我闲的无聊。来做kd-tree的题。
kd-tree一般用来解决二维问题,当然高维也可以,时间复杂度O(玄学)O(n^((k-1)/k))
主要可用来解决两类问题:
1、最近邻点的查找(或精确查找) ——此题要求的是这个
2、范围查找(统计平面(多维空间)内的权值和等)。
结构定义如下:
struct Node{
int p[k];//维数
int s[k];//儿子
int l[k],r[k];//范围
bool operator<(const Node&A)const{
return p[nowst]
是的,不看p[],像线段树;不看l[],r[],像平衡树。个人倾向于像线段树,不过kd-tree每个节点都有其实际意义。
1、考虑建树过程:
对于一维,我们考虑BST,针对多维的时候,我们按层BST,比如k=3(p数组为坐标)
第一层:按p[0]排序,分成左右区间
第二层:按p[1]排序,分成左右区间
第三层:按p[2]排序,分成左右区间
第四层:按p[0]排序……
直到叶子结点。
画图发现,我们通过坐标点将坐标系分成了一个个举行(二维下)。
2、考虑查询:
考虑暴力查找,有个显然的最优性剪枝是先找离它近的,我们的kd-tree也是这样= =
那么能够感受出kd-tree十分玄学,本质上竟然是个暴力剪枝,想卡是很容易的。比如查询最近点,我们是通过判左右儿子的距离来定优先遍历顺序的,试想所有点等距离分布在一个圆上,圆心还有一个点。。。那么你将会把所有点遍历一遍= =|||
(貌似没有什么破的方法,因此能不写尽量就不写kd-tree吧)
3、考虑插入:
暴力插入,类似于平衡树的插入,不过每次判断要根据当前层的划分依据判断。
是的,显然要考虑重构问题,我们设一个阈值,每次达到之后暴力重构用替罪羊重构,时间复杂度是可以接受的。(然而这道题重构了并没有什么加速)
if(kd.cnt==sz){
for(int i=1;i<=sz;++i)a[i]=kd.T[i];
kd.rebuild(kd.root,1,sz,0);sz+=10000;
}
4、考虑删除(此题不用)
kd-tree的删除题目十分少,而且删除一次是O(n)的,因此不多讲2333(我也不会)
#include
using namespace std;
const int Maxn=1000005;
int n,m,nowst;
struct KD_Tree{
int cnt,root;
struct Node{
int p[2];//维数
int s[2];//儿子
int l[2],r[2];//范围
bool operator<(const Node&A)const{
return p[nowst]r)return ;
x=l+r>>1;
nowst=state;
nth_element(a+l,a+x,a+r+1);
//系统函数,左闭中闭右开,将第x大放到中间,左边比他小(无序),右边比他小(无序),复杂度O(n)?不太了解,貌似要快一些
nd=a[x];a[x].newnode(nd.p,k);
Build(a[x].s[0],k,l,x-1,state^1);
Build(a[x].s[1],k,x+1,r,state^1);
maintain(x,k);
}
bool check(Node A,Node B,int k){
for(int i=0;i