[템플릿] KD 트리

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\)的建树和原理都已经讲解完毕,大概就是这样一种==平衡+替罪羊树==的思想


三、例题

1.P4148 简单题

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\)开始向下遍历:

  1. 如果以这个子树为根的子树完全不在询问矩阵内,\(return\) \(0\)
  2. 만약 \ (\ SIM \) 완전히 심문 매트릭스 내에, \ (창 \) \ (T [K] .sum \)
  3. 이가지 경우 상기 매트릭스의 일부분으로 설명되지 않으면, 우리는 먼저 현재 노드를 결정 \ (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
*/

추천

출처www.cnblogs.com/ShuraEye/p/12069524.html