树套树

前言:

什么,你说线段树码量大?那来写平衡树吧~~~

什么,你又说平衡树码量大?那来写树套树吧~~~

如果你想,可以将以前学过的数据结构自由组合一下

不过经常会用到的只有两种——线段树套平衡树和树状数组套主席树

正文:

线段树套平衡树

假设你对平衡树的各种操作已经很熟练了(你为什么那么熟练~~~)

那么如果要你写一种数据结构支持查询区间排名,区间前驱,区间后继等

你会怎么办那

提到区间操作,很容易就会想到线段树

所以我们可以线段树套平衡树啊(当然也可以线段树套线段树)

没错,就是这么简单(真香~~~)

不过我写了好久,可能是因为它码量小

无奈被某谷数据卡常(题目来自洛谷P3380

只好吸口氧才过了~~~

这里我套的是 $fhq\ treap$ ,模板在这里

#include<cstdio>
#include<algorithm>

const int maxn=55555;
#define inf (0x7fffffff)

int get_rand()
{
    static int seed=19260817;
    return seed=(((seed^142857)+200303)*2019)%0x3f3f3f3f;
}

struct node
{
    int son[2];
    int w,key,size;
    node(){}
    node(int w):w(w)
    {
        size=1;
        key=get_rand();
        son[0]=0,son[1]=0;
    }
};

int tree_cnt;
node tree[maxn*4*18];

class fhq_treap
{

    private:

        int root;
        
        int newnode(int w)
        {
            tree[++tree_cnt]=node(w);
            return tree_cnt;
        }
        
        void update(int now)
        {
            tree[now].size=tree[tree[now].son[0]].size+tree[tree[now].son[1]].size+1;
        }

        void split(int now,int num,int &x,int &y)
        {
            if(!now)
            {
                x=0,y=0;
                return;
            }
            if(tree[now].w<=num)
            {
                x=now;
                split(tree[now].son[1],num,tree[now].son[1],y);
            }
            else
            {
                y=now;
                split(tree[now].son[0],num,x,tree[now].son[0]);
            }
            update(now);
        }

        int merge(int x,int y)
        {
            if(!x||!y) return x+y;
            if(tree[x].key<tree[y].key)
            {
                tree[x].son[1]=merge(tree[x].son[1],y);
                update(x);
                return x;
            }
            else
            {
                tree[y].son[0]=merge(x,tree[y].son[0]);
                update(y);
                return y;
            }
        }

   public:

        fhq_treap():root(0){}

        void insert(int num)
        {
            int x,y;
            split(root,num,x,y);
            root=merge(merge(x,newnode(num)),y);
        }

        void remove(int num)
        {
            int x,y,z;
            split(root,num,x,z);
            split(x,num-1,x,y);
            y=merge(tree[y].son[0],tree[y].son[1]);
            root=merge(merge(x,y),z);
        }

        int get_rank(int num)
        {
            int x,y;
            split(root,num-1,x,y);
            int rank=tree[x].size+1;
            root=merge(x,y);
            return rank;
        }

        int get_kth(int k)
        {
            int now=root,flag=0;
            while(now)
            {
                int left_size=tree[tree[now].son[0]].size;
                if(k<=left_size) now=tree[now].son[0],flag=0;
                else if(k>left_size+1) k-=left_size+1,now=tree[now].son[1],flag=1;
                else return tree[now].w;
            }
            return flag?inf:-inf;
        }

        int get_pre(int num)
        {
            int rank=get_rank(num);
            return get_kth(rank-1);
        }

        int get_suf(int num)
        {
            int rank=get_rank(num+1);
            return get_kth(rank);
        }

}treap[maxn<<2];

int n,m;
int data[maxn];

#define left(x) (x<<1)
#define right(x) (x<<1|1)

void build(int k,int l,int r)
{
    for(int i=l;i<=r;i++)
        treap[k].insert(data[i]);
    if(l==r) return;
    int mid=(l+r)>>1;
    build(left(k),l,mid);
    build(right(k),mid+1,r);
}

void update(int k,int l,int r,int pos,int num)
{
    treap[k].remove(data[pos]);
    treap[k].insert(num);
    if(l==r) return;
    int mid=(l+r)>>1;
    if(pos<=mid) update(left(k),l,mid,pos,num);
    else update(right(k),mid+1,r,pos,num);
}

int get_rank(int k,int l,int r,int x,int y,int num)
{
    if(l>y||r<x) return 0;
    if(l>=x&&r<=y) return treap[k].get_rank(num)-1;
    int mid=(l+r)>>1;
    return get_rank(left(k),l,mid,x,y,num)+get_rank(right(k),mid+1,r,x,y,num);
}

int get_kth(int x,int y,int k)
{
    int l=0,r=inf,ans=-1;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(get_rank(1,1,n,x,y,mid)+1<=k) ans=mid,l=mid+1;
        else r=mid-1;
    }
    return ans;
}

int get_pre(int k,int l,int r,int x,int y,int num)
{
    if(l>y||r<x) return -inf;
    if(l>=x&&r<=y) return treap[k].get_pre(num);
    int mid=(l+r)>>1;
    return std::max(get_pre(left(k),l,mid,x,y,num),get_pre(right(k),mid+1,r,x,y,num));
}

int get_suf(int k,int l,int r,int x,int y,int num)
{
    if(l>y||r<x) return inf;
    if(l>=x&&r<=y) return treap[k].get_suf(num);
    int mid=(l+r)>>1;
    return std::min(get_suf(left(k),l,mid,x,y,num),get_suf(right(k),mid+1,r,x,y,num));
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&data[i]);
    build(1,1,n);
    for(int i=1,opt;i<=m;i++)
    {
        scanf("%d",&opt);
        if(opt==1)
        {
            int x,y,num;
            scanf("%d%d%d",&x,&y,&num);
            printf("%d\n",get_rank(1,1,n,x,y,num)+1);
        }
        if(opt==2)
        {
            int x,y,k;
            scanf("%d%d%d",&x,&y,&k);
            printf("%d\n",get_kth(x,y,k));
        }
        if(opt==3)
        {
            int pos,num;
            scanf("%d%d",&pos,&num);
            update(1,1,n,pos,num);
            data[pos]=num;
        }
        if(opt==4)
        {
            int x,y,num;
            scanf("%d%d%d",&x,&y,&num);
            printf("%d\n",get_pre(1,1,n,x,y,num));
        }
        if(opt==5)
        {
            int x,y,num;
            scanf("%d%d%d",&x,&y,&num);
            printf("%d\n",get_suf(1,1,n,x,y,num));
        }
    }
    return 0;
}

树状数组套主席树

假设你对主席树已经很熟悉了

那么如果要你写一种数据结构支持查询动态区间第 $k$ 大

你又会怎么办那(数据保证线段树套平衡树不可过)

显然我们需要一种更优的做法

于是我们选择树状数组套主席树

$ps:$ 我还不会......

后序:

是不是感觉树套树还挺简单的

写完了树套树,你就可以写树套树套树

如果你还不满意,甚至可以写树套树套树套树套树套树套树

只要你有时间,我没意见的(逃~~~)

转载于:https://www.cnblogs.com/Vscoder/p/10639100.html

<think>嗯,用户想了解线段树套线段树的实现和应用场景。首先,我需要回忆一下什么是线段树套线段树。根据之前的引用内容,比如引用[1]提到,这是一种二维线段树的实现方法,外层处理行,内层处理列。所以,这应该用于处理二维区间的问题,比如矩阵的查询和更新。 接下来,用户可能想知道具体的实现方式。记得引用[2]说过,二维线段树是先按行构建外层线段树,每个节点再按列构建内层线段树。这样分层结构应该能处理二维区域的查询。需要详细说明外层和内层如何协作,可能还要提到每个节点的结构,比如外层节点代表行区间,内层节点处理对应的列区间。 然后应用场景方面,用户可能想知道在哪些实际问题中会用到这种结构。比如矩阵区域求和、最值查询,或者动态二维范围统计,比如游戏中的地图更新。引用[4]提到树套树属于高级数据结构,可能应用在需要高效处理二维数据的场景,比如图像处理或地理信息系统。 另外,需要考虑这种数据结构的优缺点。引用[3]提到其他结构如K-D树或二进制分组可能有不同的适用场景,所以需要对比一下。比如线段树套线段树的时间复杂度是O(logN * logM),适用于静态或动态二维数据,但可能空间消耗较大,因为每个外层节点都有内层线段树。 用户还可能想知道具体的代码结构,比如如何嵌套实现。可能需要分步骤说明,先构建外层,再在每个节点构建内层,然后处理查询和更新操作。例如,更新某个点时,先在外层找到对应的行区间,再在内层更新列的位置。查询时则需要在多个外层和内层节点上聚合结果。 还要注意正确使用LaTeX格式,比如数学表达式要用$...$,独立公式用$$...$$。比如矩阵范围求和可以写成公式,时间复杂度也要用LaTeX表示。 可能用户没有说出来的深层需求是,如何在实际项目中应用这种结构,或者其他数据结构的选择比较。因此,在回答时应该涵盖实现步骤、应用例子以及优缺点比较,帮助用户全面理解。 最后,确保引用来源正确,引用[1]和[2]是关于二维线段树的,而引用[3]和[4]提到了其他树结构和应用场景,可能需要适当引用来说明对比和适用范围。</think>### 线段树套线段树的实现应用场景 #### 一、实现方式 线段树套线段树(二维线段树)是一种嵌套结构,外层线段树管理行维度,每个外层节点再嵌套一棵内层线段树管理列维度[^1][^2]。 **实现步骤**: 1. **外层线段树**:按行构建,每个节点表示行区间 $[L, R]$ 2. **内层线段树**:在每个外层节点中按列构建,处理对应行区间内的列区间查询 3. **数据存储**:外层节点的每个叶子节点对应一行数据,其内层线段树存储该行的列数据 **示例代码框架**(伪代码): ```python class OuterNode: inner_tree = InnerSegmentTree() # 内层线段树 class TwoDimensionalSegmentTree: def build(self, rows): # 外层构建 for row in rows: outer_node = build_outer_tree(row) outer_node.inner_tree.build(row.columns) # 内层构建 def query(self, x1, x2, y1, y2): # 外层查询行区间 [x1, x2],内层查询列区间 [y1, y2] return outer_query(x1, x2).inner_query(y1, y2) def update(self, x, y, value): # 外层定位行x,内层更新列y outer_locate(x).inner_update(y, value) ``` #### 二、时间复杂度分析 - **单点更新**:$O(\log N \cdot \log M)$(外层树深度 $\log N$,内层树深度 $\log M$) - **区域查询**:$O(\log N \cdot \log M)$ - **空间复杂度**:$O(N \cdot M)$(最坏情况下) #### 三、应用场景 1. **动态矩阵区域操作** - 实时更新矩阵元素值 - 查询子矩阵的和/最大值,例如: $$ \sum_{i=a}^{b} \sum_{j=c}^{d} matrix[i][j] $$ 2. **地理信息系统** - 动态统计矩形区域内的点数量(如地图热点分析) 3. **图像处理** - 对图像局部区域进行像素值统计或滤波操作[^2] #### 四、其他结构的对比 | 结构类型 | 适用场景 | 时间复杂度 | 空间复杂度 | |-----------------|--------------------------|------------------|-------------| | 线段树套线段树 | 动态二维区间查询/更新 | $O(\log^2 n)$ | $O(n^2)$ | | K-D Tree | 静态高维近邻搜索 | $O(n^{1-1/d})$ | $O(n)$ | | 二维前缀和 | 静态矩阵区域求和 | $O(1)$查询 | $O(n^2)$ | #### 五、优化方向 1. **惰性标记扩展**:在内层线段树中引入惰性传播,支持区间批量更新 2. **空间压缩**:对稀疏矩阵使用动态开点技术[^3] 3. **混合结构**:对行使用线段树,对列使用树状数组以降低常数因子[^4]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值