Uva 11922 Splay
对于BST,除了Treap树之外,还有一种Splay的伸展树,他能快速的分裂与合并。
重要的操作是伸展操作,将一个指定的结点 x 旋转到根的过程。
分三种情况,一次单旋,两次同向单旋,两次反向旋转。可以手动模拟一下这个过程。
到这里,问题常常是将序列的第 k 个元素旋转到根。
首先,要知道的是伸展树里面v存的是什么,是节点的编号(下标)。这样才能像 Treap实现名次树那样,很方便的找到左边第 k 个元素。
//将序列左数第k个元素选择到根
void splay(Node* &o,int k) {
o->pushdown();
int d = o->cmp(k);
if(d==1) k-=o->ch[0]->s + 1;
if(d!=-1) {
Node* p = o->ch[d];
p->pushdown();
int d2 = p->cmp(k);
int k2 = (d2==0 ? k : k - p->ch[0]->s - 1);
if(d2!=-1) {
splay(p->ch[d2],k2);
if(d==d2) rotate(o,d^1);
else rotate(o->ch[d],d);
}
rotate(o,d^1);
}
}
分裂与合并:
分裂:从序列从左第 k 个元素分裂,就是将序列的 o 的第 K 小元素伸展到根,断开树根与右子节点。
合并:将left部分最大的元素旋转到根,将right作为 left的右子树。(保证right>left所有元素)。
// 合并操作。假定left所有元素小于 right
Node* merge(Node* left,Node* right) {
splay(left,left->s);
left->ch[1] = right;
left->maintain();
return left;
}
//把 o 前 k 个小结点放到left里面,其他放到ritht里面,如果不够right = null
void split(Node* o,int k,Node* &left,Node* &right) {
splay(o,k);
left = o;
right = o->ch[1];
o->ch[1] = null;
left->maintain();
}
有时,对于序列有反转操作,这时,利用 线段树的 lazy标记,标记某一段是否反转。
对于,数据结构的定义:用一个Node数组,和一个Node 的 root指针,指向这个数组的元素。
#include <bits/stdc++.h> using namespace std; struct Node { Node *ch[2]; int s; int flip; int v; int cmp(int k) const { int d = k - ch[0]->s; if(d==1) return -1; //序列第 k 个找到 return d <=0 ? 0 : 1; } void maintain() { s = ch[0]->s + ch[1]->s + 1; } void pushdown() { if(flip) { flip = 0; swap(ch[0],ch[1]); ch[0]->flip = !ch[0]->flip; ch[1]->flip = !ch[1]->flip; } } }; Node *null = new Node(); // d = 0 左旋 void rotate(Node* &o,int d) { Node* k = o->ch[d^1]; o->ch[d^1] = k->ch[d]; k->ch[d] = o; o->maintain(); k->maintain(); o = k; } //将序列左数第k个元素选择到根 void splay(Node* &o,int k) { o->pushdown(); int d = o->cmp(k); if(d==1) k-=o->ch[0]->s + 1; if(d!=-1) { Node* p = o->ch[d]; p->pushdown(); int d2 = p->cmp(k); int k2 = (d2==0 ? k : k - p->ch[0]->s - 1); if(d2!=-1) { splay(p->ch[d2],k2); if(d==d2) rotate(o,d^1); else rotate(o->ch[d],d); } rotate(o,d^1); } } // 合并操作。假定left所有元素小于 right Node* merge(Node* left,Node* right) { splay(left,left->s); left->ch[1] = right; left->maintain(); return left; } //把 o 前 k 个小结点放到left里面,其他放到ritht里面,如果不够right = null void split(Node* o,int k,Node* &left,Node* &right) { splay(o,k); left = o; right = o->ch[1]; o->ch[1] = null; left->maintain(); } const int maxn = 1e5+10; struct SplaySequence { int n; Node seq[maxn]; Node *root; Node* build(int sz) { if(!sz) return null; Node* L = build(sz/2); Node* o = &seq[++n]; o->v = n; o->ch[0] = L; o->ch[1] = build(sz-sz/2-1); o->flip = o ->s = 0; o->maintain(); return o; } void init(int sz) { n = 0; null->s = 0; root = build(sz); for(int i = 0; i < sz; i++) printf("%d ",seq[i].v); puts(""); } }; vector<int> ans; void print(Node* o) { if(o!=null) { o->pushdown(); print(o->ch[0]); ans.push_back(o->v); print(o->ch[1]); } } void debug(Node* o) { if(o!=null) { o->pushdown(); debug(o->ch[0]); printf("%d \n",o->v -1); debug(o->ch[1]); } } SplaySequence ss; int main() { int n,m; scanf("%d%d",&n,&m); ss.init(n+1); debug(ss.root); for(int i = 0; i < m; i++) { int a,b; scanf("%d%d",&a,&b); Node* left,*mid,*right,*o; split(ss.root,a,left,o); split(o,b-a+1,mid,right); mid->flip ^=1; ss.root = merge(merge(left,right),mid); } print(ss.root); for(int i = 1; i < (int)ans.size(); i++) printf("%d\n",ans[i]-1); return 0; }