一向对数据结构不感冒,这次有所改观
盾哥对SPLAY 的崇拜使我也开始对这种感觉还比较好用的数据结构产生了兴趣
先分析下SPLAY的特点:
1.它能维护一个序列,动态地支持一些操作(主要是区间型操作),任意时刻它的中序遍历都是当前序列,这棵树其实是以位置为“序”的
2.它可以通过提根操作完成区间型操作,这里要用到类似与线段树的LAZY标记,更基本的操作就是旋转
3.可扩展性非常好,支持非常多的操作,感觉上线段树能搞定的,splay基本上都没什么问题
4.它的常数因子非常大,O(logN)真的只是“理论”复杂度
5.标准的splay有一大堆旋转和一大堆特判,编程复杂度奇高无比,其实不实用。个人感觉裸提根(就是只用朴素的左旋右旋)也还不错,对于编程速度要求很高的各大oi比赛中,较低的编程复杂度可能会更合适
6.初始的建树如果想偷懒就直接建成一条链,然后一顿乱旋几下,而要求高点可能就要建成一棵完全二叉树了
7.由于每个点携带的信息不多,空间上比线段树什么的稍微好点
大概就这样吧,可以看到,splay还是相当不错的,下面接着看这道题
题目大意:
维护一个序列,支持下列操作:
1.插入删除;
2.翻转一段选定的序列;
3.交换两段相邻的序列;
4.一段全部增加k;
5.询问一段的最小值。
分析:
看到一大堆的操作就应该想到是到数据结构题,由于要完成区间型操作,可以会先想到线段树,但动态的插入和删除貌似用线段树不好搞定,怎么办?splay这种强大的数据结构便能让我们脱离苦海,驶向ac的彼岸
算法:
用splay维护这个序列(先搞个极小极大点),对于每个操作:
1.插入 j 到 i 后面:
首先把i提根,在新建节点 j,使得r[j]=r[i];r[i]=j; 再维护j,i的信息即可;
2.删除i:
首先把i提根,再把 i+1提根,接着把 l[i+1]=l[i],维护i+1,这样 i 就被删除了;
3.翻转(增加)区间[a,b]:
先把a-1提根,再把b+1提根,这时候,以r[l[root]]就是我们要的区间,再用线段树的lazy思想,把标记放上去并维护就可以了;
4.交换相邻两段序列[a,b],[b+1,c](所谓的revolve其实就是这个,自己想一想):
这个操作稍微麻烦一点,先把b提根,再把a-1提为b的左儿子,接着把c+1提为b的右儿子,这时把l[r[root]](即区间[b+1,c])作为l[root]的右儿子,而原来的右儿子作为root的左儿子。此时发现root和原来的l[root]这颗子树断开连接了,我们需要把此时以root为根的树的最小的点提出来作为根,再把原来的l[root]置为 root‘ 的左儿子,操作完成。
编写的时候还是出现了一些问题的,要不会像盾哥一样调到晚上一点多(结果第二天迟到了),如:
1.min数组在标记下放的是后可以同加同减,不用再从下面更新了,更新也是错的。
2.标记下方完后清空。
3.因为一开始就新建了n+2个点,所以记总点数一开始即为n+2
好了,只用了145行就搞定,而且交这道题时是1遍ac,小小的爽了一把。
代码:
一题一结构,无题不算法