K-D Tree
什么是
一种平衡树, 维护
维空间中的点的信息.
每个节点表示一个点, 每个子树表示对应的
维超长方体. 这样说可能过于抽象, 那就先分析特殊情况, 因为 的时候, 维护的信息是序列上的, 所以这时的 就是普通平衡树.
的情况, 维护的是平面上的点, 每次选一个点, 将平面分成两半, 左右子树各占存一半的点. 每次选点之前, 首先确定要分割的这个四边形, 将要存入这个子树中的所有点的横纵坐标最大值和最小值都求出来, 这 个坐标围起来的四边形就是要分割的四边形.
然后选择这个点, 看这个四边形是横着比较长还是竖着比较长, 也就是说我们要尽量保证这个四边形的长宽差越小越好. 不失一般性, 假设这个四边形是躺着的, 也就是横着比较长. 这时需要把它竖着劈成两半, 将这些点按横坐标排序, 选中间的点作为割点, 以它的横坐标为标准划一根竖直线, 将四边形分成两个部分, 左边的部分就是这个点的左子树; 右边的部分就是右子树.
实现
由于每一个节点的分割方向都可以不同, 所以不能保证中序遍历的点以某种规律单调, 因此没法用基于旋转的平衡树维护, 这时我们就想到了替罪羊树.
因为没法以中序遍历出的序列为重构之后的树的序, 因此没法想一般替罪羊的重构方式一样重构, 需要重新求每一维坐标的极差, 然后以某个维度的坐标为序排序. 所以一次重构相比一般的替罪羊是增加了一个 的, 但是根据前面链接中文章的证明, 重构加一个 并不会影响复杂度. (其实还可以使用神奇的 nth_element()
避免排序, ,找到中位数, 使一次重构还是线性的,个人非常喜欢用!).
一般来说, 在大部分需要 的题目中, , 结合具体题目分析
例题S2OJ #1438.K-D Tree
#include<bits/stdc++.h>
#define rint register int
#define endl '\n'
using namespace std;
const int N = 2e5 + 5;
const int INf = 0x3f3f3f3f;
const double a = 0.75;
int inline min(int a,int b){
return a<b?a:b;
}
int inline max(int a,int b){
return a>b?a:b;
}
int n,op,a1,a2,b1,b2,fans,root,cur,d[N],lc[N],rc[N],L[N],R[N],D[N],U[N],siz[N],sum[N],g[N],t;
struct node{int x,y,v;}s[N];
bool inline cmp1(int a,int b){
return s[a].x<s[b].x;
}
bool inline cmp2(int a,int b){
return s[a].y<s[b].y;
}
void inline dfs(int x){
if(!x)
return;
dfs(lc[x]);
g[++t]=x;
dfs(rc[x]);
return ;
}
void inline solve(int x){
siz[x]=siz[lc[x]]+siz[rc[x]]+1;
sum[x]=sum[lc[x]]+sum[rc[x]]+s[x].v;
L[x]=R[x]=s[x].x,D[x]=U[x]=s[x].y;
if(lc[x]){
L[x]=min(L[x],L[lc[x]]);
R[x]=max(R[x],R[lc[x]]);
D[x]=min(D[x],D[lc[x]]);
U[x]=max(U[x],U[lc[x]]);
}
if(rc[x]){
L[x]=min(L[x],L[rc[x]]);
R[x]=max(R[x],R[rc[x]]);
D[x]=min(D[x],D[rc[x]]);
U[x]=max(U[x],U[rc[x]]);
}
return ;
}
int inline build(int l,int r){
if(l>r) return 0;
int mid=(l+r)>>1;
double gj1=0;
double gj2=0;
double hk1=0;
double hk2=0;
for(rint i=l;i<=r;i++){
gj1+=s[g[i]].x;
gj2+=s[g[i]].y;
}
gj1/=(r-l+1);
gj2/=(r-l+1);
for(rint i=l;i<=r;i++){
hk1+=(gj1-s[g[i]].x)*(gj1-s[g[i]].x),
hk2+=(gj2-s[g[i]].y)*(gj2-s[g[i]].y);
}
if(hk1>hk2){
nth_element(g+l,g+mid,g+r+1,cmp1);
d[g[mid]]=1;
}
else{
nth_element(g+l,g+mid,g+r+1,cmp2);
d[g[mid]]=2;
}
lc[g[mid]]=build(l,mid-1);
rc[g[mid]]=build(mid+1,r);
solve(g[mid]);
return g[mid];
}
void inline rebuild(int& x){
t=0;
dfs(x);
x=build(1,t);
return ;
}
bool inline cxom(int x){
return a*siz[x]<=(double)max(siz[lc[x]],siz[rc[x]]);
}
void inseroot(int& x, int v) {
if(!x){x=v;solve(x);return ;}
if(d[x]==1){
if(s[v].x<=s[x].x) inseroot(lc[x],v);
else inseroot(rc[x], v);
}
else{
if(s[v].y <= s[x].y) inseroot(lc[x],v);
else inseroot(rc[x],v);
}
solve(x);
if(cxom(x)) rebuild(x);
return ;
}
int inline query(int x){
if(!x||a2<L[x]||a1>R[x]||b2<D[x]||b1>U[x]) return 0;
if(a1<=L[x]&&R[x]<=a2&&b1<=D[x]&&U[x]<=b2) return sum[x];
int ress=0;
if(a1<=s[x].x&&s[x].x<=a2&&b1<=s[x].y&&s[x].y<=b2)
ress+=s[x].v;
return query(lc[x])+query(rc[x])+ress;
}
signed main(){
cin>>n;
while(~scanf("%lld",&op)){
if(op==1){
cur++;
cin>>s[cur].x>>s[cur].y>>s[cur].v;
s[cur].x^=fans,s[cur].y^=fans,s[cur].v^=fans;
inseroot(root,cur);
}
if(op==2){
cin>>a1>>b1>>a2>>b2;
a1^=fans;b1^=fans;a2^=fans;b2^=fans;
printf("%lld",fans=query(root));
puts("");
}
if(op==3){exit(0);}
}
return 0;
}
本文作者:PassName
本文链接:https://www.cnblogs.com/spaceswalker/p/16511168.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步