一些数组定义
int sum,R,num[N],sz[N],son[N][2],rd[N];
s u m sum sum结点数
R R R根结点编号
n u m [ ] num[] num[]当前结点出现的次数(与该结点值相同的个数)
s z [ ] sz[] sz[]该结点的子树大小
s o n [ ] [ ] son[][] son[][]记录结点的左右儿子
r d [ ] rd[] rd[]该结点的随机值,用于维护二叉堆 H e a p Heap Heap.
向上合并更新父亲信息
il void re(int p){
sz[p]=sz[son[p][0]]+sz[son[p][1]]+num[p];
}
旋转维护Heap
il void rot(int &p,int d){
int k=son[p][d^1]; //左旋取儿子,右选取左儿子.
son[p][d^1]=son[p][d]; //更新根结点的左儿子右为k的左儿子
son[k][d]=p;//k的左儿子设为p
re(p),re(k),p=k;更新p,k信息,根更新为k.
}
- 插入
- 删除
- 根据值找排名
- 根据排名找值
- 前驱
- 后继
基本操作
void ins(int &p,int x){
if(!p){ //为空结点直接新建
p=++sum,sz[p]=num[p]=1;
v[p]=x,rd[p]=rand();return;
}
if(v[p]==x){ //重复 更新num,sz
num[p]++,sz[p]++;return;
}
int d=x>v[p]; //往子树插入
ins(son[p][d],x);
if(rd[p]<rd[son[p][d]]) rot(p,d^1); //维护Max Heap
re(p);
}
void del(int &p,int x){
if(!p) return; //为空直接返回
if(x!=v[p]) del(son[p][x>v[p]],x); //往子树找
else {
if(!son[p][0]&&!son[p][1]){ //叶子直接更新
num[p]--,sz[p]--;
if(!num[p]) p=0; //为空删除 p=0
}
else if(son[p][0]&&!son[p][1]){ //右旋找右子树
rot(p,1);
del(son[p][1],x);
}
else if(!son[p][0]&&son[p][1]){ //左旋找左子树
rot(p,0);
del(son[p][0],x);
}
else { //找到rd较大的往该子树找
int d=rd[son[p][0]]>rd[son[p][1]];
rot(p,d);del(son[p][d],x);
}
}
re(p);
}
int rk(int p,int x){
if (!p) return 0;
if (v[p]==x) return sz[son[p][0]]+1;
if (v[p]<x) return sz[son[p][0]]+num[p]+rk(son[p][1],x);
if (v[p]>x) return rk(son[p][0],x);
}
int find(int p,int x){
if (!p) return 0;
if (sz[son[p][0]]>=x) return find(son[p][0],x);
else if (sz[son[p][0]]+num[p]<x)
return find(son[p][1],x-num[p]-sz[son[p][0]]);
else return v[p];
}
int pre(int p,int x){
if (!p) return -inf;
if (v[p]>=x) return pre(son[p][0],x);
else return max(v[p],pre(son[p][1],x));
}
int suf(int p,int x){
if (!p) return inf;
if (v[p]<=x) return suf(son[p][1],x);
else return min(v[p],suf(son[p][0],x));
}
完整版
#define il inline
int sum,R,num[N],sz[N],son[N][2],rd[N],v[N];
il void re(int p){
sz[p]=sz[son[p][0]]+sz[son[p][1]]+num[p];
}
il void rot(int &p,int d){
int k=son[p][d^1];
son[p][d^1]=son[k][d];
son[k][d]=p;
re(p),re(k),p=k;
}
void ins(int &p,int x){
if(!p){
p=++sum,sz[p]=num[p]=1;
v[p]=x,rd[p]=rand();return;
}
if(v[p]==x){
num[p]++,sz[p]++;return;
}
int d=x>v[p];
ins(son[p][d],x);
if(rd[p]<rd[son[p][d]]) rot(p,d^1);
re(p);
}
void del(int &p,int x){
if(!p) return;
if(x!=v[p]) del(son[p][x>v[p]],x);
else {
if(!son[p][0]&&!son[p][1]){
num[p]--,sz[p]--;
if(!num[p]) p=0;
}
else if(son[p][0]&&!son[p][1]){
rot(p,1);
del(son[p][1],x);
}
else if(!son[p][0]&&son[p][1]){
rot(p,0);
del(son[p][0],x);
}
else {
int d=rd[son[p][0]]>rd[son[p][1]];
rot(p,d);del(son[p][d],x);
}
}
re(p);
}
int rk(int p,int x){
if (!p) return 0;
if (v[p]==x) return sz[son[p][0]]+1;
if (v[p]<x) return sz[son[p][0]]+num[p]+rk(son[p][1],x);
if (v[p]>x) return rk(son[p][0],x);
}
int find(int p,int x){
if (!p) return 0;
if (sz[son[p][0]]>=x) return find(son[p][0],x);
else if (sz[son[p][0]]+num[p]<x)
return find(son[p][1],x-num[p]-sz[son[p][0]]);
else return v[p];
}
int pre(int p,int x){
if (!p) return -inf;
if (v[p]>=x) return pre(son[p][0],x);
else return max(v[p],pre(son[p][1],x));
}
int suf(int p,int x){
if (!p) return inf;
if (v[p]<=x) return suf(son[p][1],x);
else return min(v[p],suf(son[p][0],x));
}
2.无旋FHQ Treap
按权值分裂
int R; //全局定义初始化为0
int w[N],rd[N],sz[N],son[N][2],cnt; //定义在结构体内
w [ ] w[] w[]结点权值
r d [ ] rd[] rd[]结点的修正值,维护堆
s z [ ] sz[] sz[]结点子树大小
s o n [ ] [ ] son[][] son[][]记录左右儿子
c n t cnt cnt结点总数
向上合并信息
inline void re(int x){sz[x]=sz[son[x][0]]+sz[son[x][1]]+1;}
创建新结点
inline int newnode(int x){w[++cnt]=x,sz[cnt]=1,rd[cnt]=rand();return cnt;}
分裂
void split(int i,int k,int &x,int &y){if(!i) {x=y=0;return;} //如果i为空 左右子树=0 返回
//w[i]<=k 把i丢进左子树 左子树的虚拟结点为右儿子,递归
if(w[i]<=k) x=i,split(son[i][1],k,son[i][1],y);
//否则i丢进右子树,右子树的虚拟节点为左儿子,递归
else y=i,split(son[i][0],k,x,son[i][0]);
re(i);
}
合并
int merge(int x,int y){if(!x||!y) return x+y; //有一个空值即返回另一个子树
//左子树小把右儿子和右子树合并.
if(rd[x]<rd[y]) //修正值小的作为根.
{son[x][1]=merge(son[x][1],y);re(x);return x;}
//右子树小把左儿子和左子树合并.
son[y][0]=merge(x,son[y][0]);re(y);return y;
}
- 插入
- 删除
- 值找排名
- 排名找值
- 前驱
- 后继
基本操作
void ins(int a){ int x,y; split(R,a,x,y); R=merge(merge(x,newnode(a)),y); } void del(int a){ int x,y,z; split(R,a,x,z); split(x,a-1,x,y); y=merge(son[y][0],son[y][1]); R=merge(merge(x,y),z); } int kth(int x,int k){ while(1){ if(k<=sz[son[x][0]]) x=son[x][0]; else if(k==sz[son[x][0]]+1) return w[x]; else k-=sz[son[x][0]]+1,x=son[x][1]; } } int find(int a){ int x,y; split(R,a-1,x,y); int ans=sz[x]+1; R=merge(x,y); return ans; } int pre(int a){ int x,y; split(R,a-1,x,y); int ans=kth(x,sz[x]); R=merge(x,y); return ans; } int suf(int a){ int x,y; split(R,a,x,y); int ans=kth(y,1); R=merge(x,y); return ans; }
完整版
int R;
struct Treap{
int w[N],rd[N],sz[N],son[N][2],cnt;
inline void re(int x){sz[x]=sz[son[x][0]]+sz[son[x][1]]+1;}
inline int newnode(int x){w[++cnt]=x,sz[cnt]=1,rd[cnt]=rand();return cnt;}
int merge(int x,int y){if(!x||!y) return x+y;
if(rd[x]<rd[y])
{son[x][1]=merge(son[x][1],y);re(x);return x;}
son[y][0]=merge(x,son[y][0]);re(y);return y;
}
void split(int i,int k,int &x,int &y){if(!i) {x=y=0;return;}
if(w[i]<=k) x=i,split(son[i][1],k,son[i][1],y);
else y=i,split(son[i][0],k,x,son[i][0]);
re(i);
}
void ins(int a){
int x,y;
split(R,a,x,y);
R=merge(merge(x,newnode(a)),y);
}
void del(int a){
int x,y,z;
split(R,a,x,z);
split(x,a-1,x,y);
y=merge(son[y][0],son[y][1]);
R=merge(merge(x,y),z);
}
int kth(int x,int k){
while(1){
if(k<=sz[son[x][0]]) x=son[x][0];
else if(k==sz[son[x][0]]+1) return w[x];
else k-=sz[son[x][0]]+1,x=son[x][1];
}
}
int find(int a){
int x,y;
split(R,a-1,x,y);
int ans=sz[x]+1;
R=merge(x,y);
return ans;
}
int pre(int a){
int x,y;
split(R,a-1,x,y);
int ans=kth(x,sz[x]);
R=merge(x,y);
return ans;
}
int suf(int a){
int x,y;
split(R,a,x,y);
int ans=kth(y,1);
R=merge(x,y);
return ans;
}
}T;
按size分裂
无论怎么旋转,每一个 k e y [ u ] key[u] key[u]的下标对应 s i z e [ l s o n ] + 1 size[lson]+1 size[lson]+1。
这样中序遍历就是从下标 1 1 1到 n n n的。
k e y [ u ] key[u] key[u]就是原数组下标为 s i z e [ l s o n ] + 1 size[lson]+1 size[lson]+1的值。
- 该数在原数组中的下标= s i z e [ l s o n ] + 1 size[lson]+1 size[lson]+1。
struct Treap{ //fhq treap (split by rank)
int w[N],rd[N],sz[N],son[N][2],cnt;
bool lz[N];
inline void re(int x){sz[x]=sz[son[x][0]]+sz[son[x][1]]+1;} //向上合并信息
inline int ins(int x)
//插入结点
{w[++cnt]=x,sz[cnt]=1,rd[cnt]=rand();return cnt;}
void down(int x){//do something
if(lz[x]){ //
swap(son[x][0],son[x][1]);
lz[son[x][0]]^=1;
lz[son[x][1]]^=1;
lz[x]=0;
}}
int merge(int x,int y){if(!x||!y) return x+y;down(x),down(y);
if(rd[x]<rd[y])
{son[x][1]=merge(son[x][1],y);re(x);return x;}
son[y][0]=merge(x,son[y][0]);re(y);return y;
}
void split(int i,int k,int &x,int &y){if(!i) {x=y=0;return;}down(i);
if(sz[son[i][0]]<k) x=i,split(son[i][1],k-sz[son[i][0]]-1,son[i][1],y);
else y=i,split(son[i][0],k,x,son[i][0]);
re(i);
}
void fun(int x){ //inorder traversal
if(!x) return;down(x);
fun(son[x][0]);printf("%d ",w[x]);fun(son[x][1]);
}
}T;
int n,m,R=0;
例题
P3369 【模板】普通平衡树
有旋Treap和FHQ-VAL-Treap 都可。
P6136 【模板】普通平衡树(数据加强版)
同上。
P2343 宝石管理系统
插入+第 k k k大操作,同上。
P1503 鬼子进村
FHQ-VAL-Treap
用栈维护恢复操作。
房子被炸了就插入
询问就是后继-前驱-1,注意用一个数组标记该房子是否被炸,如果被炸了询问的答案为0。
先加入哨兵结点 0 , n + 1 0,n+1 0,n+1。
// Problem: P1503 鬼子进村
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1503
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// Date: 2021-02-26 12:17:32
// --------by Herio--------
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=5e4+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb emplace_back
void Print(int *a,int n){
for(int i=1;i<n;i++)
printf("%d ",a[i]);
printf("%d\n",a[n]);
}
int R;
struct Treap{ //fhq treap (split by rank wtihout rotation)
int w[N],rd[N],sz[N],son[N][2],cnt;
bool lz[N];
inline void re(int x){sz[x]=sz[son[x][0]]+sz[son[x][1]]+1;}
inline int newnode(int x){w[++cnt]=x,sz[cnt]=1,rd[cnt]=rand();return cnt;}
void ins(int a){
int x,y;
split(R,a,x,y);
R=merge(merge(x,newnode(a)),y);
}
void del(int a){
int x,y,z;
split(R,a,x,z);
split(x,a-1,x,y);
y=merge(son[y][0],son[y][1]);
R=merge(merge(x,y),z);
}
void down(int x){//do something
if(lz[x]){
swap(son[x][0],son[x][1]);
lz[son[x][0]]^=1;
lz[son[x][1]]^=1;
lz[x]=0;
}}
int merge(int x,int y){if(!x||!y) return x+y;
if(rd[x]<rd[y])
{son[x][1]=merge(son[x][1],y);re(x);return x;}
son[y][0]=merge(x,son[y][0]);re(y);return y;
}
void split(int i,int k,int &x,int &y){if(!i) {x=y=0;return;}
/*(if split by value)
modify: if(w[i]<=k) x=i,split(son[i][1],k,son[i][1],y);
*/
if(w[i]<=k) x=i,split(son[i][1],k,son[i][1],y);
else y=i,split(son[i][0],k,x,son[i][0]);
re(i);
}
//(if split by value)
int kth(int x,int k){
while(1){
if(k<=sz[son[x][0]]) x=son[x][0];
else if(k==sz[son[x][0]]+1) return w[x];
else k-=sz[son[x][0]]+1,x=son[x][1];
}
}
int pre(int a){
int x,y;
split(R,a-1,x,y);
int ans=kth(x,sz[x]);
R=merge(x,y);
return ans;
}
int suf(int a){
int x,y;
split(R,a,x,y);
int ans=kth(y,1);
R=merge(x,y);
return ans;
}
void fun(int x){ //inorder traversal
if(!x) return;down(x);
fun(son[x][0]);printf("%d ",w[x]);fun(son[x][1]);
}
}T;
int vis[N];
int main(){
int n,m;scanf("%d%d",&n,&m);
T.ins(0),T.ins(n+1);
stack<int>st;
while(m--){
char op[3];int x;
scanf("%s",op);
if(op[0]=='D'){
scanf("%d",&x);T.ins(x);st.push(x);
vis[x]=1;
}
else if(op[0]=='R'){
if(!st.empty()){
x=st.top();
T.del(x);vis[x]=0;
st.pop();
}
}
else {
scanf("%d",&x);
if(vis[x]) printf("0\n");
else printf("%d\n",T.suf(x)-T.pre(x)-1);
}
}
return 0;
}
P1533 可怜的狗狗
FHQ Treap
区间排序,然后离线处理,双指针插入删除,这样可以实现区间第 k k k大了。
for(int i=1;i<=m;i++) scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].k),q[i].id=i;
sort(q+1,q+m+1);
int L=1,RR=0;
for(int i=1;i<=m;i++){
int l=q[i].l,r=q[i].r,k=q[i].k;
while(RR<r) T.ins(a[++RR]);
while(L<l) T.del(a[L++]);
ans[q[i].id]=T.kth(R,k);
}
P1801 黑匣子
插入+全局第 k k k大 sb fhqTreap
P2073 送花
支持 插入+删除+全局第 k k k大
fhq treap 即可。
维护总美丽值和价格,用两个变量即可。
因为最后的花的价格都不同,所以用一个map维护是否重复即可。
即价格唯一映射美丽值。
较水。
while(~scanf("%d",&op)&&~op){
if(op==1){
int w,c;scanf("%d%d",&w,&c);
if(mp[c]) continue;
mp[c]=w,T.ins(c);++tot;sum+=w;res+=c;
}
else if(op==2){
if(!tot) continue;
int w=T.kth(R,tot);
T.del(w);tot--;sum-=mp[w];mp[w]=0;res-=w;
}
else if(op==3){
if(!tot) continue;
int w=T.kth(R,1);
T.del(w);tot--;sum-=mp[w];mp[w]=0;res-=w;
}
}
printf("%lld %lld\n",sum,res);
按SIZE分裂的习题
区间翻转 P3391 【模板】文艺平衡树
区间翻转等价于该子树的左右儿子结点向下交换。
首先按照 l − 1 l-1 l−1分裂,得到 [ 1 , l − 1 ] [1,l-1] [1,l−1] 和 [ l , n ] [l,n] [l,n]。
然后再从 [ l , n ] [l,n] [l,n]分裂出size大小为 r − l + 1 r-l+1 r−l+1的,注意是按 s i z e size size分裂的,所以选出前 r − l + 1 r-l+1 r−l+1个就对应的该区间。
然后用线段树思想 懒标记处理一下就行了。
// Problem: P3391 【模板】文艺平衡树
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3391
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// Date: 2021-07-19 18:25:05
// --------by Herio--------
// Problem: P3391 【模板】文艺平衡树
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3391
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// Date: 2021-02-25 23:20:48
// --------by Herio--------
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=1e5+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb emplace_back
void Print(int *a,int n){
for(int i=1;i<n;i++)
printf("%d ",a[i]);
printf("%d\n",a[n]);
}
struct Treap{ //fhq treap (split by rank)
int w[N],rd[N],sz[N],son[N][2],cnt;
bool lz[N];
inline void re(int x){sz[x]=sz[son[x][0]]+sz[son[x][1]]+1;}
inline int ins(int x){w[++cnt]=x,sz[cnt]=1,rd[cnt]=rand();return cnt;}
void down(int x){//do something
if(lz[x]){
swap(son[x][0],son[x][1]);
lz[son[x][0]]^=1;
lz[son[x][1]]^=1;
lz[x]=0;
}}
int merge(int x,int y){if(!x||!y) return x+y;down(x),down(y);
if(rd[x]<rd[y])
{son[x][1]=merge(son[x][1],y);re(x);return x;}
son[y][0]=merge(x,son[y][0]);re(y);return y;
}
void split(int i,int k,int &x,int &y){if(!i) {x=y=0;return;}down(i);
if(sz[son[i][0]]<k) x=i,split(son[i][1],k-sz[son[i][0]]-1,son[i][1],y);
else y=i,split(son[i][0],k,x,son[i][0]);
re(i);
}
void fun(int x){ //inorder traversal
if(!x) return;down(x);
fun(son[x][0]);printf("%d ",w[x]);fun(son[x][1]);
}
}T;
int n,m,R=0;
#define getchar()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<21],*p1=buf,*p2=buf;
template <typename T>
inline T& read(T& r) {
r = 0; bool w = 0; char ch = getchar();
while(ch < '0' || ch > '9') w = ch == '-' ? 1 : 0, ch = getchar();
while(ch >= '0' && ch <= '9') r = r * 10 + (ch ^ 48), ch = getchar();
return r = w ? -r : r;
}
int main(){
read(n),read(m);
for(int i=1;i<=n;i++) R=T.merge(R,T.ins(i));
while(m--){
int l,r,x,y,z;read(l),read(r);
T.split(R,l-1,x,y);
T.split(y,r-l+1,y,z);
T.lz[y]^=1;
R=T.merge(x,T.merge(y,z));
}
T.fun(R);
return 0;
}