---
# System prepended metadata

title: TREAP

---

# TREAP
## 特性
treap=tree+heap
### tree
指binary search tree
可以直接在上面二分搜
![](https://upload.wikimedia.org/wikipedia/commons/d/da/Binary_search_tree.svg)
### heap
每個節點都比他的子節點大(或小)
![](https://upload.wikimedia.org/wikipedia/commons/c/c4/Max-Heap-new.svg)
### treap
![](https://upload.wikimedia.org/wikipedia/commons/e/e4/Treap.svg)
每個節點會有:
pri值(縱軸): heap特性，為了保持平衡而隨機決定
key值(橫軸): tree特性，其實就是原序列中的位置
以及其他你想維護的資訊
```cpp=
struct treap{
    int pri,key,val;
    treap *lc,*rc;
    treap(int _key,int _val):key(_key),val(_val){
        pri=rand();
        lc=rc=nullptr;
    }
};
```
treap 的操作都是把要做事的區間split 出來，詢問或操作完成後再merge回去
## split
![](https://upload.wikimedia.org/wikipedia/commons/6/69/Treap_split.svg)
```cpp=
void split(treap* t,int k,treap*& l,treap*& r){
    //把t中key值小於等於k的分到l，其他分到r
    if(!t) l=r=nullptr;
    else if(t->key<=k){
        l=t;
        split(t->rc, k, l->rc, r);
    }
    else{
        r=t;
        split(t->lc, k, l, r->lc);
    }
}
```
## merge
![](https://upload.wikimedia.org/wikipedia/commons/a/a8/Treap_merge.svg)
```cpp=
treap* merge(treap* a,treap* b){
    if(!a||!b) return a? a:b;
    if(a->pri>b->pri){
        a->rc=merge(a->rc,b);
        return a;
    }
    else{
        b->lc=merge(a,b->lc);
        return b;
    }
}
```
只要在用的時候確保a->key小於b->key，就只要檢查heap的特性就好(即pri值的大小)
## 應用
反正就記住把要做事的區間split 出來，詢問或操作完成後再merge回去
:::warning
:warning:常數通常比線段樹大，斟酌使用
:::
### 插入
```cpp=
treap insert(treap *t,int idx,int k){
    treap *a,*b;
    split(t,idx,a,b);
    return merge(a,merge(new treap(idx,k),b));
}
```
### 刪除
```cpp=
treap remove(treap *t,int idx){
    treap *a,*b;
    split(t,idx-1,a,t);
    split(t,0,t,b);
    return merge(a,b);
}
```
### RMQ
[Dynamic Range Minimum Queries](https://cses.fi/problemset/task/1649)
加個pull就好了
sum, XOR, gcd也都差不多
:::spoiler code:
```cpp=
#include<bits/stdc++.h>
using namespace std;
struct treap{
    int pri,key,val,mn;
    treap *lc,*rc;
    treap(int _key,int _val):key(_key),val(_val){
    	mn=val;
        pri=rand();
        lc=rc=nullptr;
    }
};
void pull(treap *&a){
	a->mn=a->val;
	if(a->lc) a->mn=min(a->mn,a->lc->mn);
	if(a->rc) a->mn=min(a->mn,a->rc->mn);
}
void split(treap *t,int k,treap *&l,treap *&r){
    if(!t) l=r=nullptr;
    else if(t->key<=k){
        l=t;
        split(t->rc, k, l->rc, r);
        pull(l);//注意這邊
    }
    else{
        r=t;
        split(t->lc, k, l, r->lc);
        pull(r);//注意這邊
    }
}
treap* merge(treap *a,treap *b){
    if(!a||!b) return a? a:b;
    if(a->pri>b->pri){
        a->rc=merge(a->rc,b);
        pull(a);//注意這邊
        return a;
    }
    else{
        b->lc=merge(a,b->lc);
        pull(b);//注意這邊
        return b;
    }
}
void upd(treap *&t,int idx,int k){
	treap *a,*b;
	split(t,idx-1,a,t);
	split(t,idx,t,b);
	t->val=t->mn=k;
	t=merge(a,merge(t,b));
}
int query(treap *&t,int l,int r){
	treap *a,*b;
	split(t,l-1,a,t);
	split(t,r,t,b);
	int ret=t->mn;
	t=merge(a,merge(t,b));
	return ret;
}
int main(){
	int n,q;
	cin>>n>>q;
	treap *tr=NULL;
	for(int i=0;i<n;i++){
		int a;
		cin>>a;
		tr=merge(tr,new treap(i,a));
	}
	while(q--){
		int a,b,c;
		cin>>a>>b>>c;
		if(a==1){
			b--;
			upd(tr,b,c);
		}
		else{
			b--;
			c--;
			cout<<query(tr,b,c)<<endl;
		}
	}
	return 0;
}
```
:::
### 關於key
如果要做的操作會改變節點在原序列的位置怎麼辦，這樣key不就亂掉了嗎

每次移動就更改key?
如果要搬一大段就會需要每一個都改，太麻煩了，需要一個更好維護並且可以推出key的資訊
沒錯就是size

key就是整個**treap中有多少人比他小**
以根節點為例，他的key就是左子節點的size
如果在左邊，就直接遞迴往下
如果在右邊，key就是根節點的左子節點的size + 1 + 遞迴往下做的答案
:::spoiler 利用size:
```cpp=
struct treap{
	int pri,sz,val;
    treap *lc,*rc;
    treap(int _val):val(_val){
        pri=rand();
        sz=1;
        lc=rc=nullptr;
    }
};
void pull(treap *t){
    t->sz=1;
    if(t->lc) t->sz+=t->lc->sz;
    if(t->rc) t->sz+=t->rc->sz;
}
void split(treap *t,int idx,treap *&l,treap *&r){
    int lsize=t->lc? t->lc->sz:0;
    if(!t) l=r=NULL;
    else if(lsize<=idx){
        l=t;
        split(t->rc,idx-lsize-1,l->rc,r);
        pull(l);
    }
    else{
        r=t;
        split(t->lc,idx,l,r->lc);
        pull(r);
    }
}
treap* merge(treap* a,treap* b){
    if(!a||!b) return a? a:b;
    if(a->pri>b->pri){
        a->rc=merge(a->rc,b);
        pull(a);
        return a;
    }
    else{
        b->lc=merge(a,b->lc);
        pull(b);
        return b;
    }
}
```
:::
### 懶標
和線段樹一樣，紀錄**該節點已經做完，子節點還沒**的操作
在此以區間加值，區間求和為例
```cpp=
void push(treap *t){
    if(t->lz==0) return;
    if(t->lc){
        t->lc->val+=t->lz;
        t->lc->sum+=t->lz*t->lc->sz;
        t->lc->lz+=t->lz;
    }
    if(t->rc){
        t->rc->val+=t->lz;
        t->rc->sum+=t->lz*t->rc->sz;
        t->rc->lz+=t->lz;
    }
}
void split(treap *t,int k,treap *&l,treap *&r){
    if(!t) l=r=nullptr;
    else if(t->key<=k){
        l=t;
        push(l);//注意這邊
        split(t->rc, k, l->rc, r);
        pull(l);
    }
    else{
        r=t;
        push(r);//注意這邊
        split(t->lc, k, l, r->lc);
        pull(r);
    }
}
treap* merge(treap *a,treap *b){
    if(!a||!b) return a? a:b;
    if(a->pri>b->pri){
        push(a);//注意這邊
        a->rc=merge(a->rc,b);
        pull(a);
        return a;
    }
    else{
        push(b);//注意這邊
        b->lc=merge(a,b->lc);
        pull(b);
        return b;
    }
}
```
### 區間反轉
區間反轉就是把要轉的區間split出來，然後把左右子樹對調，子樹也遞迴做一樣的事
然後再加上懶標就好了

## 題目

[Cut and Paste](https://cses.fi/problemset/task/2072)
:::spoiler code(先自己寫看看)
```cpp=
#include<bits/stdc++.h>
using namespace std;
struct treap{
    int pri,sz,val;
    treap *lc,*rc;
    treap(int _val):val(_val){
        pri=rand();
        sz=1;
        lc=rc=nullptr;
    }
};
void pull(treap *t){
    t->sz=1;
    if(t->lc) t->sz+=t->lc->sz;
    if(t->rc) t->sz+=t->rc->sz;
}
void split(treap *t,int idx,treap *&l,treap *&r){
    if(!t) l=r=NULL;
    else{
        int lsize=t->lc? t->lc->sz:0;
        if(lsize<=idx){
            l=t;
            split(t->rc,idx-lsize-1,l->rc,r);
            pull(l);
        }
        else{
            r=t;
            split(t->lc,idx,l,r->lc);
            pull(r);
        }
    }
}
treap* merge(treap* a,treap* b){
    if(!a||!b) return a? a:b;
    if(a->pri>b->pri){
        a->rc=merge(a->rc,b);
        pull(a);
        return a;
    }
    else{
        b->lc=merge(a,b->lc);
        pull(b);
        return b;
    }
}
int main(){
    int n,q;
    cin>>n>>q;
    treap *tr=NULL;
    string s;
    cin>>s;
    for(int i=0;i<n;i++){
        tr=merge(tr,new treap(s[i]-'A'));
    }
    while(q--){
        int a,b;
        cin>>a>>b;
        a--;
        b--;
        treap *left,*right;
        split(tr,a-1,left,tr);
        split(tr,b-a,tr,right);
        tr=merge(left,merge(right,tr));
    }
    for(int i=0;i<n;i++){
        treap *ans;
        split(tr,0,ans,tr);
        char c='A'+ans->val;
        cout<<c;
    }
    return 0;
}
```
:::
---
[Substring Reversals](https://cses.fi/problemset/task/2073)
:::spoiler code(先自己寫看看)
```cpp=
#include<bits/stdc++.h>
using namespace std;
struct treap{
    int pri,sz,val;
    bool lz;
    treap *lc,*rc;
    treap(int _val):val(_val){
        lz=0;
        pri=rand();
        sz=1;
        lc=rc=nullptr;
    }
};
void push(treap *t){
    if(!t->lz) return;
    if(t->lc){
        swap(t->lc->lc,t->lc->rc);
        t->lc->lz^=1;
    }
    if(t->rc){
        swap(t->rc->lc,t->rc->rc);
        t->rc->lz^=1;
    }
    t->lz=0;
}
void pull(treap *t){
    t->sz=1;
    if(t->lc) t->sz+=t->lc->sz;
    if(t->rc) t->sz+=t->rc->sz;
}
void split(treap *t,int idx,treap *&l,treap *&r){
    if(!t) l=r=NULL;
    else{
        int lsize=t->lc? t->lc->sz:0;
        if(lsize<=idx){
            l=t;
            push(l);
            split(t->rc,idx-lsize-1,l->rc,r);
            pull(l);
        }
        else{
            r=t;
            push(r);
            split(t->lc,idx,l,r->lc);
            pull(r);
        }
    }
}
treap* merge(treap* a,treap* b){
    if(!a||!b) return a? a:b;
    if(a->pri>b->pri){
        push(a);
        a->rc=merge(a->rc,b);
        pull(a);
        return a;
    }
    else{
        push(b);
        b->lc=merge(a,b->lc);
        pull(b);
        return b;
    }
}
int main(){
    int n,q;
    cin>>n>>q;
    treap *tr=NULL;
    string s;
    cin>>s;
    for(int i=0;i<n;i++){
        tr=merge(tr,new treap(s[i]-'A'));
    }
    while(q--){
        int l,r;
        cin>>l>>r;
        l--;
        r--;
        treap *left,*right;
        split(tr,l-1,left,tr);
        split(tr,r-l,tr,right);
        swap(tr->lc,tr->rc);
        tr->lz^=1;
        tr=merge(left,merge(tr,right));
    }
    for(int i=0;i<n;i++){
        treap *ans;
        split(tr,0,ans,tr);
        char c='A'+ans->val;
        cout<<c;
    }
    return 0;
}
```
:::
---
[Reversals and Sums](https://cses.fi/problemset/task/2074)
:::spoiler code(先自己寫看看)
我還沒寫
2023.05.16 更 寫完了，我也不知道為什麼拖這麼久
```cpp=
#include<bits/stdc++.h>
#define int long long
using namespace std;
struct treap{
    int pri,sz,val,sum;
    bool lz;
    treap *lc,*rc;
    treap(int _val):val(_val){
        lz=0;
        sum=val;
        pri=rand();
        sz=1;
        lc=rc=nullptr;
    }
};
void push(treap *t){
    if(!t->lz) return;
    if(t->lc){
        swap(t->lc->lc,t->lc->rc);
        t->lc->lz^=1;
    }
    if(t->rc){
        swap(t->rc->lc,t->rc->rc);
        t->rc->lz^=1;
    }
    t->lz=0;
}
void pull(treap *t){
    t->sum=t->val;
    t->sz=1;
    if(t->lc){
        t->sz+=t->lc->sz;
        t->sum+=t->lc->sum;
    }
    if(t->rc){
        t->sz+=t->rc->sz;
        t->sum+=t->rc->sum;
    }
}
void split(treap *t,int idx,treap *&l,treap *&r){
    if(!t) l=r=NULL;
    else{
        int lsize=t->lc? t->lc->sz:0;
        if(lsize<=idx){
            l=t;
            push(l);
            split(t->rc,idx-lsize-1,l->rc,r);
            pull(l);
        }
        else{
            r=t;
            push(r);
            split(t->lc,idx,l,r->lc);
            pull(r);
        }
    }
}
treap* merge(treap* a,treap* b){
    if(!a||!b) return a? a:b;
    if(a->pri>b->pri){
        push(a);
        a->rc=merge(a->rc,b);
        pull(a);
        return a;
    }
    else{
        push(b);
        b->lc=merge(a,b->lc);
        pull(b);
        return b;
    }
}
signed main(){
    int n,q;
    cin>>n>>q;
    treap* tr=NULL;
    for(int i=0;i<n;i++){
        int a;
        cin>>a;
        tr=merge(tr,new treap(a));
    }
    while(q--){
        int a;
        cin>>a;
        if(a==1){
            int l,r;
            cin>>l>>r;
            l--;
            r--;
            treap *left,*right;
            split(tr,l-1,left,tr);
            split(tr,r-l,tr,right);
            swap(tr->lc,tr->rc);
            tr->lz^=1;
            tr=merge(left,merge(tr,right));
        }
        else{
            int l,r;
            cin>>l>>r;
            l--;
            r--;
            treap *left,*right;
            split(tr,l-1,left,tr);
            split(tr,r-l,tr,right);
            cout<<tr->sum<<endl;
            tr=merge(left,merge(tr,right));
        }
    }
    return 0;
}
```
:::

---
[SuperMemo](https://vjudge.net/problem/POJ-3580) 序列操作大雜燴
