思路
A操作由于是对全局节点增加一个值,显然暴力修改不可行,我们考虑使用一个temp保存A和S对数据的加减。由于有一个temp,所以I操作我们需要插入的val改为val-temp。对S,我们插入一个暂时的min-temp,然后删除左子树,然后删除之前插入的那个点。F命令使用FindByRank函数,略微修改下内部参数即可(因为原来是第k多)。本题要注意BZOJ该题使用cin,cout会RE。
Splay
参考了Menci和xehoth的splay模板。吐槽一下这模板怎么删子树是直接改指针的啊?下面的节点直接不回收了。。虽然速度是很快就是了。之前写过一个旋转式的Treap,Splay与它唯一的不同就是Splay操作了,所以这里就记了个内存静态化和Splay操作。
内存静态化
避免了new指针导致空间占用过大的问题1
2
3
4
5
6
7
8
9
10
11
12
13
14template<typename T, size_t MAXSIZE>
struct MemoryPool {
T buf[MAXSIZE], *tail, *ext[MAXSIZE];
int top;
void init() {
tail = buf, top = 0;
}
T* newnode() {
return top ? ext[--top] : tail++;
}
void retnode(T* rub) {
ext[top++] = rub;
}
};
Splay操作
Splay在求前趋,后继,插入,删除的时候都要把修改的节点旋转到根上,使得每个操作的期望复杂度都是log。下面定义目标节点为根(或者是任意你想旋转的节点)的父亲。Splay分三种情况:
- 祖父节点为目标节点直接旋转上去
- 祖父和父亲的左右儿子关系和父亲与自己左右儿子关系相同,先旋转父亲,再旋转自己
- 不然自己做两次旋转
为什么要分2,3的情况?事实上这被称作Splay的双旋,是保证期望时间复杂度为log的重要基础。具体见伸展树splay单旋PK双旋。
参考
代码
1 |
|