关于Splay的几个灵魂问题
写着一篇文章的目的在于,我被几道带有lazy标记的题折磨了很久。
1. Splay是在维护节点值的顺序吗?
我们先来看一看常规的splay定义:
const int N = 1e5+5;
struct Node{
int s[2]; // 左右儿子
int p; // 父节点
int v; // 节点值
int size; // 子树大小
}tr[N];
请问,splay是在维护节点的信息v
吗?要回答这个问题,我们应该看看影响splay平衡性的两个rotate
,splay
:
int ws(int x){
return tr[tr[x].p].s[1] == x;
}
void push_up(int x){
tr[x].size = tr[tr[x].s[0]].size + tr[tr[x].s[1]].size + 1;
}
void rotate(int x){
int y = tr[x].p;
int z = tr[y].p;
int k = ws(x);
tr[z].s[ws(y)] = x;
tr[y].p = x;
tr[y].s[k] = tr[x].s[k^1];
tr[tr[x].s[k^1]].p = y;
tr[x].p = z;
tr[x].s[k^1] = y;
push_up(y);
push_up(x);
}
void splay(int x, int k){
while(tr[x].p != k){
int y = tr[x].p;
int z = tr[y].p;
if(z != k){
if(ws(x) ^ ws(y)){
rotate(x);
}else{
rotate(y);
}
}
rotate(x);
}
if(!k) root = x;
}
可以看出,splay在旋转时并不会像其他平衡树(如AVL)一样对节点值进行比较。事实上,splay并不会维护节点值的大小顺序,而是在维护整棵树的中序遍历不变。
2. Splay的lazy什么时候需要push_down?
push_down
会影响该节点的子树的信息。一方面是在需要访问子树信息时push_down
,这个自然不必多提;另一方面,如果push_down
操作会改变树的结构,如交换左右子树,而查询操作要求获得某一侧子树的大小,或判断该节点是哪一个儿子(这个很常见),则必须先push_down
。
我们以flip_lazy
为例,给出两种风格的lazy
,请选择合适的一种使用:
-
push_down
不改变本节点信息void push_down(int x){ if(tr[x].lazy){ if(tr[x].s[0]){ swap(tr[tr[x].s[0]].s[0], tr[tr[x].s[0]].s[1]); tr[tr[x].s[0]].lazy ^= 1; } if(tr[x].s[1]){ swap(tr[tr[x].s[1]].s[0], tr[tr[x].s[1]].s[1]); tr[tr[x].s[1]].lazy ^= 1; } tr[x].lazy = 0; } } void set_lazy(int x){ tr[x].lazy ^= 1; swap(tr[x].s[0], tr[x].s[1]); }
-
push_down
改变本节点信息void push_down(int x){ if(tr[x].lazy){ swap(tr[x].s[0], tr[x].s[1]); if(tr[x].s[0]) tr[tr[x].s[0]].lazy ^= 1; if(tr[x].s[1]) tr[tr[x].s[1]].lazy ^= 1; tr[x].lazy = 0; } } void set_lazy(int x){ tr[x].lazy ^= 1; }
此外,rotate
,splay
,getk
,get_rank
操作都需要push_down
void rotate(int x){
int y = tr[x].p;
int z = tr[y].p;
push_down(y);
push_down(x);
int k = ws(x);
tr[z].s[ws(y)] = x;
tr[y].p = x;
tr[y].s[k] = tr[x].s[k^1];
tr[tr[x].s[k^1]].p = y;
tr[x].p = z;
tr[x].s[k^1] = y;
push_up(y);
push_up(x);
}
void splay(int x, int k){
while (tr[x].p != k) {
int y = tr[x].p;
int z = tr[y].p;
push_down(z);
push_down(y);
push_down(x);
if(z != k){
if(ws(x) ^ ws(y)){
rotate(x);
}else{
rotate(y);
}
}
rotate(x);
}
if(!k) rt = x;
}
int build(int l, int r, int p){
if(l > r) return 0;
else if(l == r){
tr[l].lazy = 0;
tr[l].p = p;
tr[l].v = arr[l];
tr[l].s[0] = tr[l].s[1] = 0;
tr[l].size = 1;
return l;
}else{
int mid = (l+r)>>1;
tr[mid].lazy = 0;
tr[mid].p = p;
tr[mid].v = arr[mid];
tr[mid].s[0] = build(l, mid-1, mid);
tr[mid].s[1] = build(mid+1, r, mid);
push_up(mid);
return mid;
}
}
int getk(int k){
int u = rt;
while(u){
push_down(u);
if(tr[tr[u].s[0]].size >= k){
u = tr[u].s[0];
}else if(tr[tr[u].s[0]].size + 1 == k){
return u;
}else{
k -= tr[tr[u].s[0]].size + 1;
u = tr[u].s[1];
}
}
return -1;
}
int get_rank(int u){
splay(u, 0);
push_down(u);
return tr[tr[u].s[0]].size;
}