首先借助Google大叔吧这道题看懂了。
和【noi维护数列】比较相像,可以说是其简化版本,需要维护的东西少一点,操作要多一个。
简要的来说,他是要支持者样几种操作:
1.插入删除;
2.翻转一段选定的序列;
3.交换两段相邻的序列;
4.一段全部增加k;
5.询问一段的最小值。
前一段时间学习了一下Splay,亮点就在与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]:
这个操作稍微麻烦一点,先把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。
Splay一共5种旋转,写起来稍麻烦,特判比较多,程序一下子就丑了起来,于是抱着侥幸的心理,编写了只有左旋右旋两种旋转的‘Splay’(酷似treap);编起来非常舒服,最后111行写完了,交程序的时候,AC的字样着实让我兴奋了下,下面是记录:
Run ID | User | Problem | Result | Memory | Time | Language | Code Length | Submit Time |
7196764 | cjLD | 3580 | Accepted | 5016K | 1266MS | Pascal | 2689B | 2010-07-19 22:13:35 |
速度是慢了点,这你别怪我,但是2689B的长度到是非常可观,真真考试的时候还是要考虑下编程复杂度的。
关于专门卡的数据也不怕,只要在每次操作后random一个数提根,还是很有效的。
贴代码: