树套树

  树套树,顾名思义,就是用一颗树套在另外一组树之上。比方说我们有一颗树,假如它的每一个结点(抽象意义上,一株树由多个结点组成)也是一株树,那么这就形成了树套树。外部树和内部树可以是不同品种的。一般外部树都是形如线段树或树状数组等用于统计和区间处理但是内存消耗大的树,而内部树则往往是像Splay一样功能强大且内存消耗小的树,当然这不是绝对的。

  考虑这样的一个场景,给你一组数值序列S={v1,v2,....},要求你快速求区间[L,R]之间第k小的值,并且数值序列中可能有若干元素会被修改。如何解决?首先我们建立一株线段树,线段树内每个区间(对应原线段树结点)均用Splay树维护,区间[x,y]对应的Splay树用于保存子序列S[x...y]中的值。显然一次更新操作,我们只需要更新log2(n)个区间,对应的也就是log2(n)个区间,更新操作可以用删除原Splay结点并插入一个新的结点来实现,因此一次操作的空间复杂度为O(log2(n)),当然时间复杂度为O(log2(n)*log2(n))。而一次询问操作,我们只需要对可能的值做二分查找,因此问题就转化成了询问S[L...R]中小于某个特定值v的值数目。而具体的实现是,对于线段树我们仅查找区间[L,R]下对应的多棵Splay树,统计每一株树中小于v的结点数目,就可以得到正确结果。因此询问操作时间复杂度为O(log2(|V|))*log2(n)*log2(n))。其中V表示值域,由于值域较难控制,我们也可以选择用线段树维护值信息,而用Splay维护区间统计信息,这样时间复杂度就为O((log2(n))^3),但是这一般需要提前对数据(包括初始数据和后面修改的所有数据)进行预先离散化处理,相应的消耗的空间也会更大。

  也许你会问,为什么要用Splay树作为内部树,Splay树在上面场景中不就是用于统计区间信息吗,为什么不直接使用线段树套线段树呢。这是因为在创建外部树时,我们就会创建内部结点,而如果内部结点是空Splay树,每个结点消耗的空间都是常数,如果是线段树,那么消耗的空间就是O(n),这样由于外部树有O(n)个内部树结点,而每棵内部树的空间复杂度都是O(n),那么空间复杂度将达到O(n^2)。当然你可以选择先建立一株空树,之后多株线段树在空树的基础上进行可持久化修改,这样初始化的空间复杂度与线段树套Splay树一致,但是每次后续修改操作其花费的空间复杂度为O(log2(n)*log2(n)),比线段树套Splay树的O(log2(n))要大。

  树套树的玩法应该还有很多,以后遇到了再补上。

原文地址:https://www.cnblogs.com/dalt/p/8284640.html

时间: 2024-11-10 14:50:14

树套树的相关文章

BZOJ_3196_二逼平衡树(树套树:线段树+Treap)

描述 可以处理区间问题的平衡树. 分析 树套树.可以用线段树套Treap.人生第一道树套树的题... op1:如果在整区间,直接在该区间的treap上求解.否则分两个区间求解,然后相加.最后+1. op2:这个不太好直接做,可以二分,每次假定一个值,用这个值去做op1,以此求得一个rank=k+1的数,求rank=k的数等价与求这个数的前驱pre. op3:先删后加. op4&op5:如果在整区间,直接在该区间的treap上求解,否则分量个区间求解,pre取最大值,suc取最小值.注意有些数在有

BZOJ 3110: [Zjoi2013]K大数查询 [树套树]

3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6050  Solved: 2007[Submit][Status][Discuss] Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M接下来M行,每行形如1 a

[BZOJ3110] [Zjoi2013] K大数查询 (树套树)

Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M 接下来M行,每行形如1 a b c或2 a b c Output 输出每个询问的结果 Sample Input 2 5 1 1 2 1 1 1 2 2 2 1 1 2 2 1 1 1 2 1 2 3 Sample Output 1 2 1 HINT [

bzoj 3295: [Cqoi2011]动态逆序对(树套树 or CDQ分治)

Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. Input 输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数.以下n行每行包含一个1到n之间的正整数,即初始排列.以下m行每行一个正整数,依次为每次删除的元素. Output 输出包含m行,依次为删除每个元素之前,逆序对的个数. Sample Input 5 4 1 5 3

BZOJ3295 动态逆序对 树套树, 树状数组套线段树(主席树)

Orz黄学长,蒟蒻在黄学长的带领下,通过阅读黄学长的代码!终于会了这道题! 首先我想先说一下这道题的思路(准确来说是黄学长的). 很明显,树状数组应该不用讲吧!关键是内存怎么开,维护一些什么样的数据? 其实我们通过观察,很快可以发现,你维护被删的数比维护所有的数轻松多了(不管是空间上,还是时间上).所以我们就可以从这方面想!(其实我一开始的思路,因为这道题我已经看过很久了,一直想写,毕竟是白书里面的一道例题嘛!一开始,蒟蒻的我是打算这样的用树状数组套权值线段树,并且是维护所有的数,我发现空间不够

HDU HDOJ5412(树套树

题目:要求支持带修改维护区间第k大的值.所谓的动态区间第k大. 思路:题解说的是树状数组套treap,然而没想通树状数组怎么维护...线段树的话就是把所有的值离散化一下,离线建个关于值的线段树,每个节点是一个treap,treap里的值用位置做关键字,然后做区间查询,复杂度是O(nlogn*logn).基本也是经典的树套树做法....然后赛后写了两遍都没过.....今天心血来潮再挑战一下,结果从8点调到晚上1点.其间各种爆内存各种re各种t.....随机数据对拍了好久没拍出问题来....然后一直

bzoj 1901: Zju2112 Dynamic Rankings(树套树)

1901: Zju2112 Dynamic Rankings 经典的带修改求区间第k小值问题 树套树模板,我是用的线段树套splay实现的,而且用的数组模拟的,所以可能空间略大,bzoj过了,zoj过不了. 思路很简单,用线段树维护区间,用splay维护区间内的权值,然后询问的时候,二分答案key,然后在区间内找小于key的数有多少个. 贴上模板: #include<stdio.h> #include<string.h> #include<algorithm> #def

POJ 1195 2维线段树(树套树实现) 树状数组

1: #include <stdio.h> 2: #include <string.h> 3: #include <stdlib.h> 4: #include <algorithm> 5: #include <iostream> 6: using namespace std; 7:   8: #define LL(a) a<<1 9: #define RR(a) a<<1|1 10: const int MaxL = 10

[树套树]K大数查询

有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少.为了强制在线,每一次的a,b是加密的,需要异或lastans的后8位进行解密,其中lastans为上次输出的结果,初始为零.如果解密后a>b则先交换a,b数据保证解密后a,b不会超过N如果解密后a,b出现0,则赋值为1. 来历:bzoj上的一道题,经过子祯学长的魔改后,数据范围变得很奇怪... 算法:树

ZOJ 2112 Dynamic Rankings(主席树套树状数组+静态主席树)

题意:给定一个区间,求这个区间第k大的数,支持单点修改. 思路:主席树真是个神奇的东西.........速度很快但是也有一个问题就是占用内存的很大,一般来说支持单点修改的主席树套树状数组空间复杂度为O(n*logn*logn), 如果查询较少的话,可以初始的时候用一颗静态主席树,这样空间复杂度可以降为O(n*logn+q*logn*logn),勉强可以过zoj这道题. 这道题看了好久好久才懂...网上题解一般就几句话.......下面是自己的一些理解. 首先静态主席树这个东西其实比较好懂,就是对