無回転Treap狂から不回転へ
15887 ワード
バランスツリーの部分を勉強しているとき、隣のC姓dalaoはTreapに夢中になっていたが、私はSplayの優美さに夢中になった.これはTreapに対する無関心を招いた.
これは何の役に立つのか,そんなに多くの操作をしても磁器にはならない,low
今はにぎやかで、バランスのとれた木を使う問題にぶつかるたびに、悔恨の涙(汗)がこぼれてきた.
Splayは本当に難しいですね.
だから、今日は、無回転Treapについてお話ししましょう.
スピンフリーTreap
スピンフリーTreapの最大の利点は、符号化が簡単で、サポートされている操作が多く、それに対応して、Splayより少し遅いことです.
無回転Treapの主な操作は2つあり、
ぶんれつ
Treapの
同時にオブジェクトメンテナンスにより分裂後の2つのtreapのそれぞれの位置関係は
結合
Treapのマージ操作は、2本のTreapを一定の順序で組み合わせ、その最終形態は2つの側面によって決定される:
挿入
位置による挿入
まず挿入する位置の左側の部分を分割し、次に左側の部分を順番に結合し、ノード、右側の部分を挿入します.
ウェイト値による挿入
同様に,v vより小さいノードを左サブツリーに置き,順次マージする.
削除
場所による削除
削除位置を抽出し、左右のノードをマージ
権限値による削除
k以下のノードを抽出する、k-1以下の部分を抽出すると、k kに等しい値がどれだけあるかにより、kに等しいサブツリーを左右のサブノードに直接結合することで、1つのノードのみを削除することが保証される.すべて削除するには、左右の部分を直接マージします.
くかんそうさ
じっこう
BZOJ 3223文芸バランスツリー
LG-2464[SDOI 2008]うっとうしいJちゃん
これは何の役に立つのか,そんなに多くの操作をしても磁器にはならない,low
今はにぎやかで、バランスのとれた木を使う問題にぶつかるたびに、悔恨の涙(汗)がこぼれてきた.
Splayは本当に難しいですね.
だから、今日は、無回転Treapについてお話ししましょう.
スピンフリーTreap
スピンフリーTreapの最大の利点は、符号化が簡単で、サポートされている操作が多く、それに対応して、Splayより少し遅いことです.
無回転Treapの主な操作は2つあり、
split
とmerge
で、or o rを分裂して2本のTreapを合併することができる.この2つの操作により、多くの他の操作を拡張できます.ぶんれつ
Treapの
split
は、あるTreapにおいてnum n u mの大きさの左のサブツリーを分裂させることを支持し、あるいはkよりも重み値の小さいサブツリーを分裂させることを支持する.Treapは二叉探索ツリーの性質を有するため、size
or o r key
によって再帰的に分裂すればよい同時にオブジェクトメンテナンスにより分裂後の2つのtreapのそれぞれの位置関係は
size
で分裂するvoid split(int now,int num,int &x,int &y) {
if(!now) x=y=0;
else {
pushdown(now);
if(num<=siz[L]) {y=now;split(L,num,x,L);}
else {x=now;split(R,num-siz[L]-1,R,y);}
update(now);
}
}
key
で分裂void split(int now,int k,int &x,int &y) {
if(!now) x=y=0;
else {
// k , key
if(kelse {x=now;split(R,k,R,y);}
update(now);
}
}
結合
Treapのマージ操作は、2本のTreapを一定の順序で組み合わせ、その最終形態は2つの側面によって決定される:
とpos
(私は通常それを自己平衡権と呼ぶ)明らかに、Treapの原理に基づいて、通常pos
はより小さな位置に置かれ、同時にtreapは二叉探索木でもある.その相対位置が両者の親子関係(左サブツリーor o r右サブツリーに属する)を決定する左バイアスツリーと同様に再帰的にマージすればよいint merge(int a,int b) {
if(a*b==0) return a+b;
if(pos[a]1]=merge(ch[a][1],b);
update(a);
return a;
}
else {
ch[b][0]=merge(a,ch[b][0]);
update(b);
return b;
}
}
挿入
split
とmerge
によって、insert
を簡単に実現することができます.位置による挿入
まず挿入する位置の左側の部分を分割し、次に左側の部分を順番に結合し、ノード、右側の部分を挿入します.
ウェイト値による挿入
同様に,v vより小さいノードを左サブツリーに置き,順次マージする.
void insert(int v) {
int a,b;
split(rt,v,a,b);
rt=merge(merge(a,make_node(v)),b);
}
削除
split
とmerge
によって、del
を簡単に実現することができます.場所による削除
削除位置を抽出し、左右のノードをマージ
権限値による削除
k以下のノードを抽出する、k-1以下の部分を抽出すると、k kに等しい値がどれだけあるかにより、kに等しいサブツリーを左右のサブノードに直接結合することで、1つのノードのみを削除することが保証される.すべて削除するには、左右の部分を直接マージします.
void del(int v) {
int a,b,c;
split(rt,v,a,b);
split(a,v-1,a,c);
c=merge(ch[c][0],ch[c][1]);
rt=merge(merge(a,c),b);
}
くかんそうさ
splay
と同様に、split
を使用して区間を抽出し、マークを付け、最後にmerge
例えば、区間反転void rev(int l,int r) {
split(rt,r+1,a,b);
split(a,l,c,d);
rev[d]^=1;
swap(ch[d][0],ch[d][1]);
}
じっこう
BZOJ 3223文芸バランスツリー
#include
using namespace std;
#define L ch[now][0]
#define R ch[now][1]
#define mid ((l+r)>>1)
typedef pair <int,int> p;
const int N = 100010;
int n,m,rt;
struct treap {
int pos[N],ch[N][2],siz[N],rev[N],rt,key[N];
void update(int now) {siz[now]=siz[L]+siz[R]+1;}
void Rev(int now) {swap(L,R);}
void pushdown(int now) {if(rev[now]) {rev[now]^=1;rev[L]^=1;rev[R]^=1;Rev(L);Rev(R);}}
int merge(int a,int b) {
if(a*b==0) return a?a:b;
pushdown(a);pushdown(b);
if(pos[a]1]=merge(ch[a][1],b);
update(a);
return a;
}
else {
ch[b][0]=merge(a,ch[b][0]);
update(b);
return b;
}
}
void split(int now,int num,int &x,int &y) {
if(!now) x=y=0;
else {
pushdown(now);
if(num<=siz[L]) {y=now;split(L,num,x,L);}
else {x=now;split(R,num-siz[L]-1,R,y);}
update(now);
}
}
int build(int l,int r) {
if(l==r) {pos[l]=rand();siz[l]=1;key[l]=l-1;return l;}
return merge(build(l,mid),build(mid+1,r));
}
void print(int now) {
pushdown(now);
if(L) print(L);
if(key[now]>=1 && key[now]<=n) printf("%d ",key[now]);
if(R) print(R);
}
}t;
int read() {
int _ans=0,_flag=1;
char _ch=getchar();
while((_ch != '-') && (_ch > '9' || _ch < '0')) _ch=getchar();
if(_ch == '-') {_flag = -1;_ch = getchar();}
while(_ch >= '0' && _ch <= '9') {_ans=_ans*10+_ch-'0';_ch=getchar();}
return _ans*_flag;
}
int main() {
n=read();m=read();
rt=t.build(1,n+2);
for(int i=1;i<=m;++i) {
int l=read(),r=read(),a,b,c,d;
t.split(rt,r+1,a,b);
t.split(a,l,c,d);
t.rev[d]^=1;
t.Rev(d);
rt=t.merge(c,t.merge(d,b));
}
t.print(rt);
return 0;
}
LG-2464[SDOI 2008]うっとうしいJちゃん
#include
using namespace std;
#define L ch[now][0]
#define R ch[now][1]
const int N = 400010;
map <int,int> p;
struct Treap {
int key[N],pos[N],cnt,ch[N][2],rt[N],siz[N];
int newnode(int k) {pos[++cnt]=rand();siz[cnt]=1;key[cnt]=k;return cnt;}
void update(int now) {siz[now]=1+siz[L]+siz[R];}
void split(int now,int k,int &x,int &y) {
if(!now) {x=y=0;return;}
if(kelse {x=now;split(R,k,R,y);}
update(now);
}
int merge(int a,int b) {
if(a*b==0) return a+b;
if(pos[a]1]=merge(ch[a][1],b);
update(a);
return a;
}
else {
ch[b][0]=merge(a,ch[b][0]);
update(b);
return b;
}
}
void insert(int k,int v) {
int a,b;
split(rt[k],v,a,b);
rt[k]=merge(a,merge(newnode(v),b));
}
void del(int k,int v) {
int a,b,c;
split(rt[k],v,a,b);
split(a,v-1,a,c);
c=merge(ch[c][0],ch[c][1]);
rt[k]=merge(merge(a,c),b);
}
void query(int k,int l,int r) {
int a,b,c;
split(rt[k],r,a,b);
split(a,l-1,a,c);
printf("%d
",siz[c]);
rt[k]=merge(merge(a,c),b);
}
}t;
int n,m,cnt,a[N];
int read();
int main() {
srand(time(NULL));
n=read();m=read();
for(int i=1;i<=n;++i) {
a[i]=read();
if(p[a[i]]==0) p[a[i]]=++cnt;
a[i]=p[a[i]];
t.insert(a[i],i);
}
for(int i=1;i<=m;++i) {
char ch=getchar();
while(ch!='Q' && ch!='C') ch=getchar();
if(ch=='Q') {
int l=read(),r=read(),k=read();
k=p[k];
t.query(k,l,r);
}
else {
int pos=read(),k=read();
t.del(a[pos],pos);
if(p[k]==0) p[k]=++cnt;
a[pos]=p[k];
t.insert(a[pos],pos);
}
}
return 0;
}
int read() {
int _ans=0,_flag=1;
char _ch=getchar();
while((_ch != '-') && (_ch > '9' || _ch < '0')) _ch=getchar();
if(_ch == '-') {_flag = -1;_ch = getchar();}
while(_ch >= '0' && _ch <= '9') {_ans=_ans*10+_ch-'0';_ch=getchar();}
return _ans*_flag;
}