A는 \ (KD-트리 \) 을 수행하는 데 사용됩니다
\ (KD-트리 \) :케빈 듀 랜트 트리아니,이, 다시
\ (KD-트리 \) : \ (K-차원은 \) 나무를 언급
좋은 영어 학생들이 알 (차원 \) \ 는 IS 크기 때문에, 의미는, \ (KD-트리 \) 문자 그대로 수단 : \ (K \) 차원 트리
여기, 우리는 알아야한다 \ (KD-트리 \) 을 수행하는 데 사용됩니다
예, 유지하고 쿼리를 다차원 큐브 데이터를 저장하는 데 사용됩니다
이 시점에서, 다차원 데이터의 문제는 확실히 많은 학생들, 괴롭혀 \ (KD-트리 \) 이러한 문제를 해결하기 위해 설계되었습니다
그러나 곤약 FOUND 주류 기본적인 시험 방법은 테스트하는 것입니다 (\ 2) \ 에 대한이 이야기 그래서, 차원 (\ 2) \ 차원의 접근 방식 (사실 PA, 그는 다차원하지 않습니다)
두, \ (KD-트리 \) 무엇 인
\ (KD-나무 \) A는 \ (BST \) (이진 검색 트리) 변종
\ (은 \를 BST) 은 무엇입니까?
\ (BST \) 왼쪽 서브 트리의 주요 노드가 적은 현재 노드의 키보다 길어야 오른쪽 하위 트리 키워드의 키 노드는 현재 노드보다 커야합니다
\ (BST는 \) 실제로 나눌 수있는 한 차원 일
그래서 \ (KD-트리 \) 에 \ (K \) 일 분할의 치수
글쎄, 우리 모두 알고는, \ (KD-트리 \) 가 (K \) \ (예 : 치수) 키워드.
이제 우리는 고려할 필요가있는 각 층이 분할하는 키워드를 기반으로해야한다는 것입니다?
하나 개의 전략은 가장 개방 각 차원 분산액이 시점에서 더 분할 될 수 있음을 나타내는, 각 차원의 분산을 계산 최대 편차 것을 선택하는 평형 점
그러나에서 \ (\ 2) 평화 유지의 경우, 우리는 일반적으로합니다 \ (2 \) 차원 회전에 사용
어떤 키워드 각 계층 후 좋은 언론의 결정에, 우리는 키워드를 기준으로 정렬하고, 현재의 열 수를 가져 가리 것이다 중간 통과 할 다음 층을 그 하나, 그
우리는 언론 모르는 \ (2D-트리 \) 를 보는 경우를
우리는 (손으로 그린 볼이 너무 어려운 일이 아니다)이 차트를 볼 수 있습니다
우리는 먼저 눌러 고려 (X \) \ 의 가장 중간을 얻기 위해, 분할에 \ ((8,2) \)
그럼에 따라 \ (Y \) 로 이동 \ ((4,6) \) 와 \ ((10,3) \)
再按照\(x\)来
此时只需要划分右边的区间,取到\((11,7)\)
这样,一棵\(2d-tree\)就建好啦,建出来应该是这样的:
我们可以发现,这样建出来的\(kd-tree\)因为每次取中间数,所以建出来的树的叶子节点的深度都十分接近,可以近乎平衡,但是,并不是所有情况都可以保证平衡,所以,我们采用如替罪羊树一样的方法将不平衡的子树拍扁重建,这样使得树的高度在\(nlog_{n}\sim n\sqrt{n}\)之间(但是它十分不稳定...)
\(kd-tree\)的建树和原理都已经讲解完毕,大概就是这样一种==平衡+替罪羊树==的思想
三、例题
Description
内存限制为20MB
你有一个\(N×N\)的棋盘,每个格子内有一个整数,初始时的时候全部为\(0\),现在需要维护两种操作:
命令 参数限制 内容
\(1\) \(x\) \(y\) \(A\)
\(1≤x,y≤N\),\(A\)是正整数 将格子\(x,y\)里的数字加上\(A\)
\(2\) \(x_{1}\) \(y_{1}\) \(x_{2}\) \(y_{2}\)
\(1≤x1≤x2≤N,1≤y1≤y2≤N\) 输出x1 y1 x2 y2这个矩形内的数字和
3 无 终止程序
\(Input\)
输入文件第一行一个正整数\(N\)。
接下来每行一个操作。每条命令除第一个数字之外,
均要异或上一次输出的答案\(lastans\),初始时\(lastans=0\)。
\(Output\)
对于每个\(2\)操作,输出一个对应的答案。
\(Sample Input\)
4
1 2 3 3
2 1 1 3 3
1 1 1 1
2 1 1 0 7
3
\(Sample Output\)
3
5
\(HINT\)
\(1≤N≤500000\),操作数不超过\(200000\)个,内存限制\(20M\),保证答案在\(int\)范围内并且解码之后数据仍合法。
Source
练习题 树8-8-KD-tree
思路
一道\(kd-tree\)模板题(要不然我为什么要放在第一题 )
我们考虑对于每一个操作\(2\),我们都将这个节点的\(x,y,val\)打包成一个\(struct\)插入\(kd-tree\)的每一个节点中,并维护五个值:\(maxx,maxy,minx,miny,sum\),前四个分别代表以这个节点为根节点的子树中的每个节点的\(x,y\)的最大值和最小值,\(sum\)代表以这个节点为根的子树中的权值和
考虑对于操作\(3\)
我们从根节点\(k\)开始向下遍历:
- 如果以这个子树为根的子树完全不在询问矩阵内,\(return\) \(0\)
- 만약 \ (\ SIM \) 완전히 심문 매트릭스 내에, \ (창 \) \ (T [K] .sum \)
- 이가지 경우 상기 매트릭스의 일부분으로 설명되지 않으면, 우리는 먼저 현재 노드를 결정 \ (k는 \) 은 플러스 경우, 매트릭스에 \ (K \) 자중 후 이송 왼쪽 하위 트리 대답은 합계입니다
자세한 내용은 코드를 볼 수
#include<bits/stdc++.h>
using namespace std;
const int N=200005;
const double alpha=0.725;
int n,rt,nodetot=0,topp=0,cnt=0;
struct point
{
int x[2],val;
}p[N];
struct tree
{
int lc,rc,siz,sum;
int maxn[2],minn[2];
point pt;
}t[N];
int WD;
int rub[N];
bool cmp(point a,point b)
{
return a.x[WD]<b.x[WD];
}
int newnode()
{
if(topp)return rub[topp--];
return ++nodetot;
}
void update(int k)
{
t[k].siz=t[t[k].lc].siz+t[t[k].rc].siz+1;
t[k].sum=t[t[k].lc].sum+t[t[k].rc].sum+t[k].pt.val;
for(int i=0;i<=1;i++)
{
t[k].maxn[i]=t[k].minn[i]=t[k].pt.x[i];
if(t[k].lc)
{
t[k].maxn[i]=max(t[k].maxn[i],t[t[k].lc].maxn[i]);
t[k].minn[i]=min(t[k].minn[i],t[t[k].lc].minn[i]);
}
if(t[k].rc)
{
t[k].maxn[i]=max(t[k].maxn[i],t[t[k].rc].maxn[i]);
t[k].minn[i]=min(t[k].minn[i],t[t[k].rc].minn[i]);
}
}
}
bool bad(int k)//如替罪羊树一样判断是否平衡
{
return (t[k].siz*alpha<t[t[k].lc].siz||t[k].siz*alpha<t[t[k].rc].siz);
}
void work(int k)
{
if(t[k].lc)work(t[k].lc);
p[++cnt]=t[k].pt;
rub[++topp]=k;//将不用的节点编号存进rub中,节省空间
if(t[k].rc)work(t[k].rc);
}
int build(int l,int r,int wd)
{
if(l>r)return 0;
int mid=(l+r)>>1,k=newnode();
WD=wd;//每次按照当前维度排序
nth_element(p+l,p+mid,p+r+1,cmp);
//这是一个神奇的STL,会使得序列a中[l,r]中的第mid小的元素在第mid位上,但是其他元素并不有序!!!
//这个STL的时间复杂度为O(n),这也是我们不使用sort的原因,并且可以去到中位数
t[k].pt=p[mid];
t[k].lc=build(l,mid-1,wd^1);
t[k].rc=build(mid+1,r,wd^1);
update(k);
return k;
}
void rebuild(int &k)
{
cnt=0;
work(k);//拍扁
k=build(1,cnt,0);//重建
}
void ins(int &k,point tmp,int wd)
{
if(!k)//新建节点
{
k=newnode();
t[k].lc=t[k].rc=0;
t[k].pt=tmp;
update(k);
return ;
}
if(tmp.x[wd]<=t[k].pt.x[wd])ins(t[k].lc,tmp,wd^1);
else ins(t[k].rc,tmp,wd^1);
//判断应该插入进左子树还是右子树中
update(k);
if(bad(k))rebuild(k);//如果不平衡,拍扁重建
}
bool out(int nx1,int nx2,int ny1,int ny2,int x1,int y1,int x2,int y2)
{
if(x1>nx2||x2<nx1||y1>ny2||y2<ny1)return 1;
return 0;
}
bool in(int nx1,int nx2,int ny1,int ny2,int x1,int y1,int x2,int y2)
{
if(nx1>=x1&&nx2<=x2&&ny1>=y1&&ny2<=y2)return 1;
return 0;
}
int query(int k,int x1,int y1,int x2,int y2)
{
if(!k)return 0;
if(out(t[k].minn[0],t[k].maxn[0],t[k].minn[1],t[k].maxn[1],x1,y1,x2,y2))return 0;
//完全在矩阵外
if(in(t[k].minn[0],t[k].maxn[0],t[k].minn[1],t[k].maxn[1],x1,y1,x2,y2))return t[k].sum;
//完全在矩阵内
int res=0;
if(in(t[k].pt.x[0],t[k].pt.x[0],t[k].pt.x[1],t[k].pt.x[1],x1,y1,x2,y2))res+=t[k].pt.val;
//当前节点在矩阵内
return query(t[k].lc,x1,y1,x2,y2)+query(t[k].rc,x1,y1,x2,y2)+res;
}
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')f=-1;
ch=getchar();
}
while(isdigit(ch))
{
x=(x<<3)+(x<<1)+(ch^48);
ch=getchar();
}
return x*f;
}
int main()
{
n=read();
int op,lastans=0,x,y,a,x1,y1;
while(1)
{
op=read();
if(op==3)return 0;
if(op==1)
{
x=read(),y=read(),a=read();
x^=lastans,y^=lastans,a^=lastans;
ins(rt,(point){x,y,a},0);
}
if(op==2)
{
x=read(),y=read(),x1=read(),y1=read();
x^=lastans,y^=lastans,x1^=lastans,y1^=lastans;
printf("%d\n",lastans=query(rt,x,y,x1,y1));
}
}
return 0;
}
/*
4
1 2 3 3
2 1 1 3 3
1 1 1 1
2 1 1 0 7
3
*/