树套树相关 √
前置技能——
一大堆的数据结构。
线段树、权值线段树、树状数组等基本的
二叉搜索树、平衡树(splay、treap(无旋)、替罪羊等)、
hash(这个不是数据结构)
各种可持久化:可持久化线段树(主席树)、可持久化权值线段树、可持久化splay
高级一点的:zkw线段树、飞旋treap、红黑树、sbt。
(其实没有必要学那么多)
(不是滑稽图)
恩,大概就是什么多了。
用处——
处理一些高级的数据结构。
(废话)
我们来看看一道题——
一段区间内,动态修改数字,并且动态求区间第k大。
离线?
强制在线!
那么怎么做?
我们看到后面的求区间第k大,我们就可以很好地想到主席树或权值线段树。
那么,我们如何解决动态修改数字的操作呢?
这个东西线段树、树状数组可以解决。
那么如何把这两个东东合并起来成为解题利器呢?
那就是树套树!
其实树套树很简单理解——
一个树里面,每个节点又有一棵树(数据结构)。
恩,就是这个东西。
我们看看,线段树可以支持修改操作,然后我们还要在这里面维护主席树,那么就可以在线段树的每一个节点上弄一颗主席树,但是,主席树的形态可能会有点难看。
由于线段树常数大,所以我们看看树状数组怎么弄。
我们任然在树状数组上建主席树,但是,树状数组不像前缀和一样,所以我们每次查询主席树就用每次向前跳lowbit这样的方法来查询即可。
然后就是这样做带修主席树的啦。
那么我们考虑再改改题面——
每次支持插入、删除、查询第k大。
那么这个我们也很好搞,像上面的方法一样:
考虑一个支持插入删除的数据结构——
平衡树!
那么我们就考虑splay等平衡树来做,每次就把主席树建立于一个splay节点上。
然后,我们就可以根据实际情况来查询。
这真的可行吗?
可能会由于splay的旋转操作所影响答案,于是我还是推荐用替罪羊树来搞。
那么treap呢?选择操作依然会影响。
那么无旋treap?常数太大了。
所以——
“替罪羊树特别棒!”
我们再来看一类问题——
我们对于一个二维平面,我们每次修改一个矩形,或询问矩形内的最大最小值。
这个显然是可以用树套树来搞。
线段树太大了,那么就树状数组套树状数组。
我们姑且认为这也是树套树。
然后搞的方法类似的。
注意:空间很大
观察到可用的状态不多,每次修改只会修改O(log ^2 n)个节点,
所以我们可以用hash来储存出现的位置
这也是为什么前置知识有hash。
总结一下——
树套树是一个很神奇的数据结构,功能很多,可以说是没有做不到,只有想不到。
唯一的缺点——就是数据结构的共同缺点加倍出现——常数极大。
所以,不到关键时刻,树套树不建议用。
真的不建议用,因为你可能不小心打错一点地方,那么就很难调,编程难度很大,而且时间、空间、常数等陷阱很可能会坑你。
于是,我们现在看看例题——
T1、【gdoi2018 day1】涛涛接苹果 from jzoj5699
Description
Input
Output
Sample Input
10 5 6
1 2 3 4 5 6 7 8 9 10
9 7
7 10
6 5
7 5
5 8
5 1
2 1
3 2
2 4
2 3 4
2 9 5
1 7 3
4 8 2
5 6 6
2 3
2 5
1 4
3 5
5 1
6 1
Sample Output
0
43
4
27
11
13
这题做法——CDQ分治+树状数组或树状数组套线段树。
首先,我们CDQ分治不讲了吧,很好想的。
树状数组套线段树也是直接做,但是我们要注意一些空间、常数之类的东西。
T2、【NOIP2016模拟7.8】Dynamic len from jzoj4594
Description
有n个数编号从0→n-1,两种操作:
Q L R:询问编号为L→R-1的数中共有多少种不同的数
M X Y:将编号为X的数改为Y
共有m个操作
Input
第一行两个数n,m
接下来m行,每行有两种形式,如题目描述
Output
对于每一个Q操作,输出相应的答案
Sample Input
7 4
1 2 1 3 2 1 4
Q 1 6
M 3 2
Q 1 6
Q 3 5
Sample Output
3
2
1
Data Constraint
30% n,m<=10000
100% n,m<=50000
数的范围<=1000000
这题做法,线段树套权值线段树。
也很好想到。
T3、【NOI2014模拟】数列 from jzoj3615
Description
给定一个长度为n的正整数数列a[i]。
定义2个位置的f值为两者位置差与数值差的和,即f(x,y)=|x-y|+|a[x]-a[y]|。
你需要写一个程序支持2种操作(k都是正整数):
Modify x k:将第x个数的值修改为k。
Query x k:询问有几个i满足f(x,i)<=k。询问不仅要考虑当前数列,还要考虑任意历史版本,即统计任意位置上出现过的任意数值与当前的a[x]的f值<=k的对数。(某位置多次修改为同样的数值,按多次统计)
Input
第1行两个整数n,q。分别表示数列长度和操作数。
第2行n个正整数,代表初始数列。
第3~q+2行每行一个操作。
Output
对于每次询问操作,输出一个非负整数表示答案。
Sample Input
3 5
2 4 3
Query 2 2
Modify 1 3
Query 2 2
Modify 1 2
Query 1 1
Sample Output
2
3
3
这题,树状数组套主席树。
不会想不到吧?
但是,综上三题,看上去都很好想到,但是,编程复杂度很高,不信你试试我没试过。
而且,在你怒码几千byte的代码时,说不定就打错那么几个小错误,然后就调了几个小时,真的很崩溃。
所以,真的不建议去死磕。(除非你是那种无脑数据结构选手)