算法学习笔记之K-D tree

前言

学了好几遍K-Dtree,然而蒟蒻还是不会写(拉跨)。

算法介绍

K-D可以用来解决K维的点统计问题,一维的K-Dtree可以理解成平衡树,二维的2-Dtree在算法竞赛中最为常用,勉强可以与cdq一起解决平面矩阵+点对的问题。

接下来疑一道例题讲解:

[SDOI2012]最近最远点对

1.建树

K-Dtree的主要思想就是对平面进行一个划分,将所有点放在一颗树中,每一个叶子节点代表一个真实的点,每一个非叶子节点代表一个矩形。

在划分的过程中,尽力保证时间复杂度的正确性,有轮换,随机,方差等建树方法,但轮换与随机最为常用。

轮换,每一次划分矩形时采用不同的维度来划分,每次求出中位数,采用nth_element来求中位数。然后划分左右子树,并递归下去。

随机,就是随机化序列后依次加入点,但也是轮换比较每一维度。

2.插入

与随机的建树相同,轮换比较每一维度。

点击查看代码
void ins(int &u,bool op){
	if(!u) return u=now,void();
	if(!op) ins(a[now].x<=a[u].x?ls[u]:rs[u],1);
	else ins(a[now].y<=a[u].y?ls[u]:rs[u],0);
	pushup(u);
}

最后要pushup更新!维护矩形的四个顶点。

3.查询

使用一个估价函数:

maxdis表示当前点到当前左右子树所在矩形的最大距离。

mindis相反,表示最小距离。

两个点实现都很有趣。

点击查看代码
double mindis(int u){
	return sqr(max(a[now].x-mxx[u],0.0)+max(mnx[u]-a[now].x,0.0))+sqr(max(a[now].y-mxy[u],0.0)+max(mny[u]-a[now].y,0.0));
}

double maxdis(int u){
	return max(sqr(a[now].x-mxx[u]),sqr(mnx[u]-a[now].x))+max(sqr(a[now].y-mxy[u]),sqr(mny[u]-a[now].y));
}

然后递归查询log次

点击查看代码
void askmin(int u){
	if(!u) return ;
	if(u!=now) ans1=min(ans1,dis(u));
	double l=mindis(ls[u]),r=mindis(rs[u]);
	if(l<r){
		if(l<ans1) askmin(ls[u]);
		if(r<ans1) askmin(rs[u]);
	}else{
		if(r<ans1) askmin(rs[u]);
		if(l<ans1) askmin(ls[u]);
	}
}

void askmax(int u){
	if(!u) return ;
	ans2=max(ans2,dis(u));
	double l=maxdis(ls[u]),r=maxdis(rs[u]);
	if(l>r){
		if(l>ans2) askmax(ls[u]);
		if(r>ans2) askmax(rs[u]);
	}else{
		if(r>ans2) askmax(rs[u]);
		if(l>ans2) askmax(ls[u]);
	}
}

完整代码:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int n,now,ls[maxn],rs[maxn],rt;
double mxx[maxn],mxy[maxn],mnx[maxn],mny[maxn],ans1=9e18,ans2;
struct node{
	double x,y;
}a[maxn];
void pushup(int u){
	mxx[u]=max(mxx[u],a[now].x);
	mxy[u]=max(mxy[u],a[now].y);
	mnx[u]=min(mnx[u],a[now].x);
	mny[u]=min(mny[u],a[now].y);
}

double sqr(double x){
	return x*x;
}

double dis(int u){
	return sqr(a[now].x-a[u].x)+sqr(a[now].y-a[u].y);
}

double mindis(int u){
	return sqr(max(a[now].x-mxx[u],0.0)+max(mnx[u]-a[now].x,0.0))+sqr(max(a[now].y-mxy[u],0.0)+max(mny[u]-a[now].y,0.0));
}

double maxdis(int u){
	return max(sqr(a[now].x-mxx[u]),sqr(mnx[u]-a[now].x))+max(sqr(a[now].y-mxy[u]),sqr(mny[u]-a[now].y));
}

void ins(int &u,bool op){
	if(!u) return u=now,void();
	if(!op) ins(a[now].x<=a[u].x?ls[u]:rs[u],1);
	else ins(a[now].y<=a[u].y?ls[u]:rs[u],0);
	pushup(u);
}

void askmin(int u){
	if(!u) return ;
	if(u!=now) ans1=min(ans1,dis(u));
	double l=mindis(ls[u]),r=mindis(rs[u]);
	if(l<r){
		if(l<ans1) askmin(ls[u]);
		if(r<ans1) askmin(rs[u]);
	}else{
		if(r<ans1) askmin(rs[u]);
		if(l<ans1) askmin(ls[u]);
	}
}

void askmax(int u){
	if(!u) return ;
	ans2=max(ans2,dis(u));
	double l=maxdis(ls[u]),r=maxdis(rs[u]);
	if(l>r){
		if(l>ans2) askmax(ls[u]);
		if(r>ans2) askmax(rs[u]);
	}else{
		if(r>ans2) askmax(rs[u]);
		if(l>ans2) askmax(ls[u]);
	}
}
int main(){
	 srand(20201213);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
        scanf("%lf%lf",&a[i].x,&a[i].y);
    random_shuffle(a+1,a+n+1);
    for(int i=1;i<=n;i++){
        mxx[i]=mnx[i]=a[i].x;
        mxy[i]=mny[i]=a[i].y;
        now++;askmin(rt);askmax(rt);ins(rt,0);
    }
	printf("%.2lf %.2lf\n",sqrt(ans1),sqrt(ans2));
	
	
	return 0;
}
posted @ 2022-03-24 11:43  SSZX_loser_lcy  阅读(58)  评论(0编辑  收藏  举报