jzoj3658文本编辑器
有旋Treap由于splay的存在而无用武之地了。
优点:代码较短,原生支持区间分裂合并,并支持可持久化。
缺点:较splay大概有2的常数。
定义请戳
split(root,x)
将树root分解为两颗树,第一颗为其中[1,x],第二颗为[x+1,size]
返回是保存上述两个根的pair。
merge(a,b)
将a,b合为一颗树,返回这棵树的根。
*何时维护堆的性质:merge合并时,判断a,b中哪个点应该为另一个点的父亲。
点的期望树高是 logn log n
区间修改就与splay等一样打lazy标记。
考虑treap的构造方法(选一个权值最大的作为根),其实树的形态就是笛卡尔树。随机序列唯一对应着一颗笛卡尔树,随机树的期望高度是 logn log n 。
证明:
设 fi f i 为有i个点的期望深度和。显然
可持久化后:
split(root,x),返回两颗新树(保有一些连回原树的边,原树不作改动)
merge(a,b),返回一颗新树(同上)
给一个点打区间标记时:需要开新点,不能改原点。这意味着down也可能要开新点。
注意一个key的问题:
size[a]>size[b]
(假的)
key[a]< key[b]
(不可持久化时用)
rand() % (size[a] + size[b]) < size[a]
(可持久化时用)
2无法在可持久化时使用是因为可能碰到自己与自己合并的情况。这样无法保证树形态随机了。因为按照大小赋权随机,大上小下即可。
据说: 不可持久化的情况下2,3是基本一致的。
#include
#include
#include
#include
#define update(x) (size[x]=size[c[x][0]]+size[c[x][1]]+1)
using namespace std;
const int N = 1e5+10,C = 190 * N;
typedef pair<int,int> D;
int tot,fa[C],c[C][2],size[C],lazy[C],root;
char val[C];
int m;
int newnode(int x = 0) {
++tot;
fa[tot] = fa[x];
size[tot] = size[x];
lazy[tot] = lazy[x];
memcpy(c[tot],c[x],sizeof c[x]);
val[tot] = val[x];
return tot;
}
int rever(int x) {
if (x) {
int w = newnode(x);
lazy[w]^=1;
swap(c[w][0],c[w][1]);
return w;
} else return 0;
}
void down(int x) {
if (x && lazy[x]) {
c[x][0] = rever(c[x][0]);
c[x][1] = rever(c[x][1]);
lazy[x] = 0;
}
}
D split(int x,int k) {
if (!x) return (D){0,0};
down(x); D y;
int z = newnode(x);
if (size[c[x][0]] >= k) {
y = split(c[x][0],k);
c[z][0] = y.second; update(z);
y.second = z;
} else {
y = split(c[x][1],k-size[c[x][0]]-1);
c[z][1] = y.first; update(z);
y.first = z;
}
return y;
}
int merge(int a,int b) {
if (!a || !b) return a+b;
down(a),down(b); int q = 0;
if (rand() % (size[a] + size[b]) <= size[a] + 15/*magic*/) {
q = newnode(a);
c[q][1] = merge(c[q][1],b);
} else {
q = newnode(b);
c[q][0] = merge(a,c[q][0]);
}
update(q);
return q;
}
char zc;
void read(int &x) {
while ((zc=getchar()) < '0' || zc > '9');
x = zc - '0';
while ((zc=getchar())>='0' && zc <='9') x = x * 10 + zc - '0';
}
int main() {
freopen("editor.in","r",stdin);
freopen("editor.out","w",stdout);
cin>>m;
for (int i = 1; i <= m; i++) {
// cout<// out(root); cout<<"::"<"\n");
char op; op = getchar();
if (op == 'I') {
int x; char ch; read(x); ch = getchar();
D y = split(root, x);
int zz = newnode(); val[zz] = ch; size[zz] = 1;
root = merge(y.first, merge(zz, y.second));
} else
if (op == 'D') {
int l,r; read(l), read(r);
D y = split(root, l - 1);
D z = split(y.second, r - l + 1);
root = merge(y.first, z.second);
} else
if (op == 'C') {
int l,r,w; read(l),read(r),read(w);
D y = split(root, l - 1);
D z = split(y.second, r - l + 1);
int k = z.first;
z = split(root, w);
root = merge(z.first, merge(k, z.second));
} else
if (op == 'R') {
int l,r; read(l),read(r);
D y = split(root, l - 1);
D z = split(y.second, r - l + 1);
root = merge(y.first, merge(rever(z.first), z.second));
} else {
int w; read(w);
D y = split(root, w - 1);
y = split(y.second, 1);
putchar(val[y.first]);
// root = merge(y.first, merge(z.first, z.second));
}
}
}