bzoj2716 天使玩偶

本文介绍了一种使用KD树进行高效点集查询的方法,通过记录每个节点的最大范围并优先搜索估值距离更小的节点,提高了查询效率。文章还讨论了在插入大量点时的树重构策略,以避免树的不平衡,确保查询性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门
用之前的那种估值函数过不去。。
要记录每个点所包含的最大范围才过得去,每次查就看查询点与这个范围的最小可能距离。搜的时候先搜估值距离小的。
如果插入的点超过一定数量就重构一下,防止过于不平衡,不过貌似并没有什么卵用。

#include<bits/stdc++.h>
#define cs const
#define re register
#define lc(x) T[x].son[0]
#define rc(x) T[x].son[1]
cs int maxD=2,N=1e6+10,oo=1e9,rebuild=3e5;
int n,m,t,ans,x,y,INS=0;
namespace IO{
	cs int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;}
	template<typename T>
	inline T get(){
		char ch;T x;
		while(!isdigit(ch=gc()));x=ch^48;
		while(isdigit(ch=gc())) x=((x+(x<<2))<<1)+(ch^48);
		return x;
	}
	inline int gi(){return get<int>();}
}
using IO::gi;
using std::max;
inline void Min(int &x,int y){if(x>y)x=y;}
inline void Max(int &x,int y){if(x<y)x=y;}
namespace KD_tree{
	#define mid (l+r>>1)
	int tot=0,cmp_d,root=0;
	struct node{
		int mx[maxD],mn[maxD],val[maxD],son[2];
		node(int X=0,int Y=0){
			mx[0]=mn[0]=val[0]=X;
			mx[1]=mn[1]=val[1]=Y;
			son[0]=son[1]=0;
		}
	}T[N];
	inline bool cmp(cs node &a,cs node &b){return a.val[cmp_d]<b.val[cmp_d];}
	inline void pushup(int root){
		if(lc(root)){
			Min(T[root].mn[0],T[lc(root)].mn[0]),Max(T[root].mx[0],T[lc(root)].mx[0]);
			Min(T[root].mn[1],T[lc(root)].mn[1]),Max(T[root].mx[1],T[lc(root)].mx[1]);
		}
		if(rc(root)){
			Min(T[root].mn[0],T[rc(root)].mn[0]),Max(T[root].mx[0],T[rc(root)].mx[0]);
			Min(T[root].mn[1],T[rc(root)].mn[1]),Max(T[root].mx[1],T[rc(root)].mx[1]);
		}
	}
	inline int build(int l,int r,int div){
		if(l>r) return 0;cmp_d=div;
		std::nth_element(T+l,T+mid,T+r+1,cmp);
		int u=mid;
		T[u].son[0]=build(l,mid-1,div^1);
		T[u].son[1]=build(mid+1,r,div^1);
		return pushup(mid),mid;
	}
	inline void ins(int &now,int x,int y,int div){
		if(!now) return void(T[now=++tot]=node(x,y));
		int w=div?y:x;
		if(w<T[now].val[div]) ins(T[now].son[0],x,y,div^1);
		else ins(T[now].son[1],x,y,div^1);
		pushup(now);
	}
	inline void insert(int x,int y){ins(root,x,y,0);}
	inline int dis(cs node &A,int x,int y,int ret=0){
		return max(0,A.mn[0]-x)+max(0,A.mn[1]-y)+max(0,x-A.mx[0])+max(0,y-A.mx[1]);
	}
	inline void ask(int pos,int x,int y){
		if(!pos) return;
		int dl=oo,dr=oo,curdis=abs(T[pos].val[0]-x)+abs(T[pos].val[1]-y);
		Min(ans,curdis);
		if(lc(pos)) dl=dis(T[lc(pos)],x,y);
		if(rc(pos)) dr=dis(T[rc(pos)],x,y);
		if(dl<dr){
			if(dl<ans) ask(lc(pos),x,y);
			if(dr<ans) ask(rc(pos),x,y);
		}
		else{
			if(dr<ans) ask(rc(pos),x,y);
			if(dl<ans) ask(lc(pos),x,y);
		}
	}
	#undef mid
}
using namespace KD_tree;
int main(){
//	freopen("3059.in","r",stdin);
	n=gi(),m=gi();
	for(int i=1;i<=n;++i) x=gi(),y=gi(),T[i]=node(x,y);
	root=build(1,n,0),KD_tree::tot=n;
	while(m--){
		t=gi();
		if(t==1){
			x=gi(),y=gi(),insert(x,y);
			if(++INS==rebuild) root=build(1,tot,0),INS=0;
		}
		if(t==2) ans=oo,x=gi(),y=gi(),ask(root,x,y),printf("%d\n",ans);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值