BZOJ 2716/2648 SJY摆棋子

Description

  这天,SJY显得无聊。在家自己玩。在一个棋盘上,有N个黑色棋子。他每次要么放到棋盘上一个黑色棋子,要么放上一个白色棋子,如果是白色棋子,他会找出距离这个白色棋子最近的黑色棋子。此处的距离是 曼哈顿距离 即(|x1-x2|+|y1-y2|) 。现在给出N<=500000个初始棋子。和M<=500000个操作。对于每个白色棋子,输出距离这个白色棋子最近的黑色棋子的距离。同一个格子可能有多个棋子。

Input

  第一行两个整数N和M;
  接下来N行,每行为一个点的坐标;
  接下来M行,每行3个数t,x,y;
  如果t=1,那么放下一个黑色棋子;
  如果t=2,那么放下一个白色棋子;

Output

  对于每个T=2 输出一个最小距离

Sample Input

2 3 1 1 2 3 2 1 2 1 3 3 2 4 2

Sample Output

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

 

你可能感兴趣的:(数据结构,kd-tree)