KD树学习笔记

清华的论文和网上的博客已经非常详细了,在此就不赘述了

 

其实它比较像可持久化线段树,只不过每一层的顺序是不同的(就是维度交替有序)

然后build、insert操作就和可持久化线段树一样了

然后还有一个重构函数(替罪羊树的思想),就是直接把一个不平衡子树的所有节点存下来,然后build一下(其实非常好写的)

(之前一直都觉得很难写,其实写出来就只有2行)

写多了就熟练了,KD树还是比较板的

KD树的复杂度瓶颈在于查询,而查询的关键在于估价函数

一般KD树T了都是估价函数的问题

注意,一定要把0号点的边界赋初值!!!

 

例题:[Sdoi2010] Hide and Seek

小猪iPig在PKU刚上完了无聊的猪性代数课,天资聪慧的iPig被这门对他来说无比简单的课弄得非常寂寞,为了消除寂寞感,他决定和他的好朋友giPi(鸡皮)玩一个更加寂寞的游戏---捉迷藏。 但是,他们觉得,玩普通的捉迷藏没什么意思,还是不够寂寞,于是,他们决定玩寂寞无比的螃蟹版捉迷藏,顾名思义,就是说他们在玩游戏的时候只能沿水平或垂直方向走。一番寂寞的剪刀石头布后,他们决定iPig去捉giPi。由于他们都很熟悉PKU的地形了,所以giPi只会躲在PKU内n个隐秘地点,显然iPig也只会在那n个地点内找giPi。游戏一开始,他们选定一个地点,iPig保持不动,然后giPi用30秒的时间逃离现场(显然,giPi不会呆在原地)。然后iPig会随机地去找giPi,直到找到为止。由于iPig很懒,所以他到总是走最短的路径,而且,他选择起始点不是随便选的,他想找一个地点,使得该地点到最远的地点和最近的地点的距离差最小。iPig现在想知道这个距离差最小是多少。 由于iPig现在手上没有电脑,所以不能编程解决这个如此简单的问题,所以他马上打了个电话,要求你帮他解决这个问题。iPig告诉了你PKU的n个隐秘地点的坐标,请你编程求出iPig的问题。

Input

第一行输入一个整数N 第2~N+1行,每行两个整数X,Y,表示第i个地点的坐标

Output

一个整数,为距离差的最小值。

Sample Input

4
0 0
1 0
0 1
1 1

Sample Output

1

Hint

对于30%的数据,N<=1000 对于100%的数据,N<=500000,0<=X,Y<=10^8 保证数据没有重点保证N>=2

 

 

注意:他们选的地点是在给出来的地点中选,不是从任意一个点出发(害得我想了好久模拟退火)

然后这就是一个KD板子题了

存个模板:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int gi()
{
	char c;int num=0,flg=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
	while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
	return num*flg;
}
#define N 500005
#define MD 2
#define lc ch[i][0]
#define rc ch[i][1]
int D,DF;
int ch[N][2],rt,tot;
int siz[N],tmp[N],cnt;
int a[N][MD],mi[N][MD],mx[N][MD];
void pushup(int i)
{
	for(int j=0;j<DF;j++){
		mi[i][j]=min(min(mi[lc][j],mi[rc][j]),a[i][j]);
		mx[i][j]=max(max(mx[lc][j],mx[rc][j]),a[i][j]);
	}
	siz[i]=siz[lc]+siz[rc]+1;
}
bool cmp(int x,int y){return a[x][D]<a[y][D];}
void build(int &i,int l,int r,int d)
{
	int mid=(l+r)>>1;D=d;
	nth_element(tmp+l,tmp+mid,tmp+r+1,cmp);
	i=tmp[mid];lc=rc=0;
	if(l<mid)build(lc,l,mid-1,(d+1)%DF);
	if(r>mid)build(rc,mid+1,r,(d+1)%DF);
	pushup(i);
}
void dfs(int i)
{
	if(!i) return;
	dfs(lc);tmp[++cnt]=i;dfs(rc);
}
void insert(int &i,int d,int *x)
{
	if(!i){
		i=++tot;siz[i]=1;
		for(int j=0;j<DF;j++)
			a[i][j]=mi[i][j]=mx[i][j]=x[j];
		return;
	}
	bool flg=(a[i][d]<=x[d]);
	insert(ch[i][flg],(d+1)%DF,x);
	pushup(i);
	if(1.0*max(siz[lc],siz[rc])>0.75*siz[i]){
		cnt=0;dfs(i);
		build(i,1,cnt,d);
	}
}
int dis(int *x,int *y)
{
	int sum=0;
	for(int j=0;j<DF;j++)
		sum+=abs(x[j]-y[j]);
	return sum;
}
int maxdis(int i,int *x)
{
	int sum=0;
	for(int j=0;j<DF;j++)
		sum+=max(abs(mi[i][j]-x[j]),abs(mx[i][j]-x[j]));
	return sum;
}
int mindis(int i,int *x)
{
	int sum=0;
	for(int j=0;j<DF;j++)
		sum+=max(max(mi[i][j]-x[j],x[j]-mx[i][j]),0);
	return sum;
}
int ans,pos;
void querymax(int i,int *x)
{
	if(!i)return;
	if(maxdis(i,x)<=ans)return;
	ans=max(ans,dis(a[i],x));
	if(lc&&rc&&maxdis(lc,x)<maxdis(rc,x))querymax(rc,x),querymax(lc,x);
	else querymax(lc,x),querymax(rc,x);
}
void querymin(int i,int *x)
{
	if(!i)return;
	if(mindis(i,x)>=ans)return;
	if(pos!=i)ans=min(ans,dis(a[i],x));
	if(lc&&rc&&mindis(lc,x)>mindis(rc,x))querymin(rc,x),querymin(lc,x);
	else querymin(lc,x),querymin(rc,x);
}
int val[MD];
int main()
{
	DF=2;
	mi[0][0]=mi[0][1]=1000000000;
	mx[0][0]=mx[0][1]=0;
	int n,i,tt,aans=1000000000;
	n=gi();
	for(i=1;i<=n;i++){
		val[0]=gi();val[1]=gi();
		insert(rt,0,val);
	}
	for(i=1;i<=n;i++){
		ans=-1;querymax(rt,a[i]);tt=ans;
		pos=i;
		ans=1000000000;querymin(rt,a[i]);
		aans=min(aans,tt-ans);
	}
	printf("%d",aans);
}

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值