非旋treap没有旋转操作,而是变成了分裂和合并两个部分。
例①:P3369 【模板】普通平衡树:
题目描述
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1。插入x数
2。删除x数(若有多个相同的数,因只删除一个)
3。查询x数的排名(排名定义为比当前数小的数的个数+1。若有多个相同的数,因输出最小的排名)
4。查询排名为xx的数
5。求x的前驱(前驱定义为小于x,且最大的数)
6。求x的后继(后继定义为大于x,且最小的数)
输入格式
第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1≤opt≤6 )
输出格式
对于操作3,4,5,63,4,5,6每行输出一个数,表示对应答案
输入输出样例
输入 #1 复制
10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
输出 #1 复制
106465
84185
492737
说明/提示
时空限制:1000ms,128M
1.n的数据范围: n≤100000
2.每个数的数据范围: [-{10} ^ 7, {10} ^ 7]
来源:Tyvj1728 原名:普通平衡树
在此鸣谢
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<deque>
#include<map>
#include<vector>
#include<cmath>
#define ll long long
#define llu unsigned ll
#define pr pair<int,int>
using namespace std;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const int maxn=100100;
const int mod=1e9+7;
struct Treap
{
int l,r;//左右子节点在数组中的下标
int val,dat;//节点关键码,权值
int si;//子树大小
}t[maxn];
int tot=0,root=0,n;
int New(int val)
{
t[++tot].val=val;
t[tot].dat=rand();
t[tot].si=1;
t[tot].l=t[tot].r=0;
return tot;
}
void update(int p)
{
t[p].si=t[t[p].l].si+t[t[p].r].si+1;
}
int _merge(int x,int y)
{
if(!x||!y) return x|y;
if(t[x].dat<t[y].dat)
{
t[x].r=_merge(t[x].r,y);
update(x);
return x;
}
else
{
t[y].l=_merge(x,t[y].l);
update(y);
return y;
}
}
pr split(int x,int k)
{
if(!x) return pr(0,0);
pr y;
if(t[t[x].l].si<k)
{
y=split(t[x].r,k-t[t[x].l].si-1);
t[x].r=y.first;
update(x);
y.first=x;
}
else
{
y=split(t[x].l,k);
t[x].l=y.second;
update(x);
y.second=x;
}
return y;
}
int get_rank_by_val(int rt,int val)
{
int ans=inf,res=0;
int x=rt;
while(x)
{
if(val==t[x].val) ans=min(ans,res+t[t[x].l].si+1);
if(val>t[x].val) res+=t[t[x].l].si+1,x=t[x].r;
else x=t[x].l;
}
return ans==inf?res:ans;
}
int get_val_by_rank(int k)
{
pr x=split(root,k-1),y=split(x.second,1);
root=_merge(_merge(x.first,y.first),y.second);
return t[y.first].val;
}
void _insert(int val)
{
int k=get_rank_by_val(root,val);
pr x=split(root,k);
int rt=New(val);
root=_merge(_merge(x.first,rt),x.second);
}
void del(int val)
{
int k=get_rank_by_val(root,val);
pr x=split(root,k-1),y=split(x.second,1);
root=_merge(x.first,y.second);
}
int get_pre(int val)
{
int ans=-inf;
int p=root;
while(p)
{
if(t[p].val<val) ans=max(ans,t[p].val),p=t[p].r;
else p=t[p].l;
}
return ans;
}
int get_next(int val)
{
int ans=inf;
int p=root;
while(p)
{
if(t[p].val>val) ans=min(ans,t[p].val),p=t[p].l;
else p=t[p].r;
}
return ans;
}
int main(void)
{
int n;
int pos,x;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&pos,&x);
if(pos==1) _insert(x);
else if(pos==2) del(x);
else if(pos==3) printf("%d\n",get_rank_by_val(root,x));
else if(pos==4) printf("%d\n",get_val_by_rank(x));
else if(pos==5) printf("%d\n",get_pre(x));
else if(pos==6) printf("%d\n",get_next(x));
}
return 0;
}
例②:P3391 【模板】文艺平衡树(Splay):
题目背景
这是一道经典的Splay模板题——文艺平衡树。
题目描述
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1
输入格式
第一行为n,m n表示初始序列有n个数,这个序列依次是 (1,2,⋯n−1,n) m表示翻转操作次数
接下来m行每行两个数 [l,r] 数据保证 1≤l≤r≤n
输出格式
输出一行n个数字,表示原始序列经过m次变换后的结果
输入输出样例
输入 #1 复制
5 3
1 3
1 3
1 4
输出 #1 复制
4 3 2 1 5
说明/提示
n,m≤100000
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<deque>
#include<map>
#include<vector>
#include<cmath>
#define ll long long
#define llu unsigned ll
#define pr pair<int,int>
using namespace std;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const int maxn=100100;
const int mod=1e9+7;
struct Treap
{
int l,r;//左右子节点在数组中的下标
int val,dat;//节点关键码,权值
int si;//子树大小
int rev;
}t[maxn];
int tot=0,root=0,n;
int newnode(int val)
{
t[++tot].val=val;
t[tot].dat=rand();
t[tot].si=1;
t[tot].l=t[tot].r=t[tot].rev=0;
return tot;
}
void update(int p)
{
t[p].si=t[t[p].l].si+t[t[p].r].si+1;
}
void re(int x)
{
if(!x) return ;
swap(t[x].l,t[x].r);
t[x].rev^=1;
}
void pushdown(int x)
{
if(!x) return ;
if(t[x].rev)
{
re(t[x].l);
re(t[x].r);
t[x].rev=0;
}
}
int _merge(int x,int y)
{
if(!x||!y) return x|y;
if(t[x].dat<t[y].dat)
{
pushdown(x);
t[x].r=_merge(t[x].r,y);
update(x);
return x;
}
else
{
pushdown(y);
t[y].l=_merge(x,t[y].l);
update(y);
return y;
}
}
pr split(int x,int k)
{
if(!x) return pr(0,0);
pr y;
pushdown(x);
if(t[t[x].l].si<k)
{
y=split(t[x].r,k-t[t[x].l].si-1);
t[x].r=y.first;
update(x);
y.first=x;
}
else
{
y=split(t[x].l,k);
t[x].l=y.second;
update(x);
y.second=x;
}
return y;
}
void build(int n)
{
for(int i=1;i<=n;i++)
root=_merge(root,newnode(i));
}
void _reverse(int l,int r)
{
pr x=split(root,l-1),y=split(x.second,r-l+1);
re(y.first);
root=_merge(_merge(x.first,y.first),y.second);
}
void dfs(int now)
{
if(!now) return ;
pushdown(now);
dfs(t[now].l);
printf("%d ",t[now].val);
dfs(t[now].r);
}
int main(void)
{
int n,m;
int x,y;
scanf("%d%d",&n,&m);
build(n);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
_reverse(x,y);
}
dfs(root);
return 0;
}
例③:P3835 【模板】可持久化平衡树:
题目背景
本题为题目 普通平衡树 的可持久化加强版。
数据已经经过强化
感谢@Kelin 提供的一组hack数据
题目描述
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作(对于各个以往的历史版本):
1。插入x数
2。删除x数(若有多个相同的数,因只删除一个,如果没有请忽略该操作)
3。查询x数的排名(排名定义为比当前数小的数的个数+1。若有多个相同的数,因输出最小的排名)
4。查询排名为x的数
5。求x的前驱(前驱定义为小于x,且最大的数,如不存在输出-2147483647)
6。求x的后继(后继定义为大于x,且最小的数,如不存在输出2147483647)
和原本平衡树不同的一点是,每一次的任何操作都是基于某一个历史版本,同时生成一个新的版本。(操作3, 4, 5, 6即保持原版本无变化)
每个版本的编号即为操作的序号(版本0即为初始状态,空树)
输入格式
第一行包含一个正整数N,表示操作的总数。
接下来每行包含三个整数,第 i 行记为 v opt, x
v表示基于的过去版本号(0≤v <i ),opt 表示操作的序号( 1≤opt≤6 ), x表示参与操作的数值
输出格式
每行包含一个正整数,依次为各个3,4,5,6操作所对应的答案
输入输出样例
输入 #1 复制
10
0 1 9
1 1 3
1 1 10
2 4 2
3 3 9
3 1 2
6 4 1
6 2 9
8 6 3
4 5 8
输出 #1 复制
9
1
2
10
3
说明/提示
数据范围:
对于100%的数据满足: 1≤n≤5⋅10e5
经实测,正常常数的可持久化平衡树均可通过,请各位放心
样例说明:
共10次操作,11个版本,各版本的状况依次是:
0。[]
1。[9]
2。[3, 9]
3。[9, 10]
4。[3, 9]
5。[9, 10]
6。[2, 9, 10]
7。[2, 9, 10]
8。[2, 10]
9。[2, 10]
10。[3, 9]
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<deque>
#include<map>
#include<vector>
#include<cmath>
#define ll long long
#define llu unsigned ll
#define pr pair<int,int>
using namespace std;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const int maxn=500100;
const int mod=1e9+7;
struct Treap
{
int l,r;//左右子节点在数组中的下标
int val,dat;//节点关键码,权值
int si;//子树大小
}t[maxn*50];
int tot=0,root[maxn],n;
int newnode(int val)
{
t[++tot].val=val;
t[tot].dat=rand();
t[tot].si=1;
t[tot].l=t[tot].r=0;
return tot;
}
void update(int p)
{
t[p].si=t[t[p].l].si+t[t[p].r].si+1;
}
int _merge(int x,int y)
{
if(!x||!y) return x|y;
if(t[x].dat<t[y].dat)
{
int p=++tot;
t[p]=t[x];
t[p].r=_merge(t[p].r,y);
update(p);
return p;
}
else
{
int p=++tot;
t[p]=t[y];
t[p].l=_merge(x,t[p].l);
update(p);
return p;
}
}
pr split(int x,int val)
{
if(!x) return pr(0,0);
pr y;
if(t[x].val<=val)
{
int p=++tot;
t[p]=t[x];
y=split(t[p].r,val);
t[p].r=y.first;
update(p);
y.first=p;
}
else
{
int p=++tot;
t[p]=t[x];
y=split(t[p].l,val);
t[p].l=y.second;
update(p);
y.second=p;
}
return y;
}
void del(int &root,int val)
{
pr x=split(root,val-1),y=split(x.second,val);
root=_merge(t[y.first].l,t[y.first].r);
root=_merge(_merge(x.first,root),y.second);
}
void _insert(int &root,int val)
{
pr x=split(root,val);
int rt=newnode(val);
root=_merge(_merge(x.first,rt),x.second);
}
int get_val_by_rank(int p,int rk)
{
if(rk==t[t[p].l].si+1) return t[p].val;
else if(t[t[p].l].si>=rk) return get_val_by_rank(t[p].l,rk);
else return get_val_by_rank(t[p].r,rk-t[t[p].l].si-1);
}
int get_rank_by_val(int &root,int val)
{
pr x=split(root,val-1);
int ans=t[x.first].si+1;
root=_merge(x.first,x.second);
return ans;
}
int get_pre(int &root,int val)
{
pr x=split(root,val-1);
if(x.first==0) return -2147483647;
int ans=get_val_by_rank(x.first,t[x.first].si);
root=_merge(x.first,x.second);
return ans;
}
int get_next(int &root,int val)
{
pr x=split(root,val);
if(x.second==0) return 2147483647;
int ans=get_val_by_rank(x.second,1);
root=_merge(x.first,x.second);
return ans;
}
int main(void)
{
int n;
int vi,pos,x;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&vi,&pos,&x);
root[i]=root[vi];
if(pos==1) _insert(root[i],x);
else if(pos==2) del(root[i],x);
else if(pos==3) printf("%d\n",get_rank_by_val(root[i],x));
else if(pos==4) printf("%d\n",get_val_by_rank(root[i],x));
else if(pos==5) printf("%d\n",get_pre(root[i],x));
else if(pos==6) printf("%d\n",get_next(root[i],x));
}
return 0;
}
实际上分裂的时候复制过节点了,合并的时候就没有必要再复制一次了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<deque>
#include<map>
#include<vector>
#include<cmath>
#define ll long long
#define llu unsigned ll
#define pr pair<int,int>
using namespace std;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const int maxn=500100;
const int mod=1e9+7;
struct Treap
{
int l,r;//左右子节点在数组中的下标
int val,dat;//节点关键码,权值
int si;//子树大小
}t[maxn*30];
int tot=0,root[maxn],n;
int newnode(int val)
{
t[++tot].val=val;
t[tot].dat=rand();
t[tot].si=1;
t[tot].l=t[tot].r=0;
return tot;
}
void update(int p)
{
t[p].si=t[t[p].l].si+t[t[p].r].si+1;
}
int _merge(int x,int y)
{
if(!x||!y) return x|y;
if(t[x].dat<t[y].dat)
{
t[x].r=_merge(t[x].r,y);
update(x);
return x;
}
else
{
t[y].l=_merge(x,t[y].l);
update(y);
return y;
}
}
pr split(int x,int val)
{
if(!x) return pr(0,0);
pr y;
if(t[x].val<=val)
{
int p=++tot;
t[p]=t[x];
y=split(t[p].r,val);
t[p].r=y.first;
update(p);
y.first=p;
}
else
{
int p=++tot;
t[p]=t[x];
y=split(t[p].l,val);
t[p].l=y.second;
update(p);
y.second=p;
}
return y;
}
void del(int &root,int val)
{
pr x=split(root,val-1),y=split(x.second,val);
root=_merge(t[y.first].l,t[y.first].r);
root=_merge(_merge(x.first,root),y.second);
}
void _insert(int &root,int val)
{
pr x=split(root,val);
int rt=newnode(val);
root=_merge(_merge(x.first,rt),x.second);
}
int get_val_by_rank(int p,int rk)
{
if(rk==t[t[p].l].si+1) return t[p].val;
else if(t[t[p].l].si>=rk) return get_val_by_rank(t[p].l,rk);
else return get_val_by_rank(t[p].r,rk-t[t[p].l].si-1);
}
int get_rank_by_val(int &root,int val)
{
pr x=split(root,val-1);
int ans=t[x.first].si+1;
root=_merge(x.first,x.second);
return ans;
}
int get_pre(int &root,int val)
{
pr x=split(root,val-1);
if(x.first==0) return -2147483647;
int ans=get_val_by_rank(x.first,t[x.first].si);
root=_merge(x.first,x.second);
return ans;
}
int get_next(int &root,int val)
{
pr x=split(root,val);
if(x.second==0) return 2147483647;
int ans=get_val_by_rank(x.second,1);
root=_merge(x.first,x.second);
return ans;
}
int main(void)
{
int n;
int vi,pos,x;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&vi,&pos,&x);
root[i]=root[vi];
if(pos==1) _insert(root[i],x);
else if(pos==2) del(root[i],x);
else if(pos==3) printf("%d\n",get_rank_by_val(root[i],x));
else if(pos==4) printf("%d\n",get_val_by_rank(root[i],x));
else if(pos==5) printf("%d\n",get_pre(root[i],x));
else if(pos==6) printf("%d\n",get_next(root[i],x));
}
return 0;
}
例④:P5055 【模板】可持久化文艺平衡树:
题目背景
这是一道模板题。
题目描述
您需要写一种数据结构,来维护一个序列,其中需要提供以下操作(对于各个以往的历史版本):
1。在第 p 个数后插入数 x 。
2。删除第 p 个数。
3。翻转区间 [l,r],例如原序列是{5,4,3,2,1},翻转区间 [2,4]后,结果是 {5,2,3,4,1}。
4。查询区间 [l,r] 中所有数的和。
和原本平衡树不同的一点是,每一次的任何操作都是基于某一个历史版本,同时生成一个新的版本(操作 4 即保持原版本无变化),新版本即编号为此次操作的序号。
本题强制在线。
输入格式
第一行包含一个正整数 N,表示操作的总数。
接下来 N 行,每行前两个整数 vi,opti,
vi表示基于的过去版本号 (0≤vi<i),
opti表示操作的序号(1≤opti≤4)。
若 opti=1,则接下来两个整数 pi,xi,表示操作为在第 pi个数后插入数 x 。
若 opti=2,则接下来一个整数 pi,表示操作为删除第 pi个数。
若 opti=3, 则接下来两个整数 li,ri,表示操作为翻转区间 [li,ri]。
若 opti=4,则接下来两个整数 li,ri,表示操作为查询区间 [li,ri]的和。
强制在线规则:
令当前操作之前的最后一次 4 操作的答案为 lastanslastans(如果之前没有 4 操作,则 lastans=0)。
则此次操作的 pi,xi或 li,ri均按位异或上 lastanslastans 即可得到真实的 pi,xi或 li,ri
输出格式
对于每个序号为 4 的查询操作,输出一行一个数表示区间的和。
输入输出样例
输入 #1 复制
10
0 1 0 1
1 1 1 2
2 4 1 2
3 1 2 0
4 4 2 1
5 3 5 7
6 4 5 6
4 1 7 1
8 3 4 6
9 4 4 1
输出 #1 复制
3
4
5
10
说明/提示
强制在线:以下针对 pi,xi,li,ri的限制均是按位异或 lastanslastans 后的限制。
对于 100% 的数据,1≤N≤2×1e5 ,-106<xi<106
假设基于的历史版本的序列长度为 len≥1,有:
若 opti=1,则 0≤pi≤len。
若 opti=2,则 1≤pi≤len。
若 opti=3,则 1≤li≤ri≤len。
若 opti=4,则 1≤li≤ri≤len。
假设基于的历史版本的序列长度为 0,有:
opti=1,pi=0。
思路:大体思路都一样。为了保证每次操作对历史版本不产生影响,在pushdown()和split()的操作时复制节点。因为每次merge()前都会split(),所以merge()时就不需要复制节点了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<deque>
#include<map>
#include<vector>
#include<cmath>
#define ll long long
#define llu unsigned ll
#define pr pair<int,int>
using namespace std;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const int maxn=200100;
const int mod=1e9+7;
struct Treap
{
int l,r;
int val,dat;
int si;
int rev;
ll sum;
}t[maxn*200];
int tot=0,root[maxn],n;
int newnode(int val)
{
t[++tot].val=val;
t[tot].sum=val;
t[tot].dat=rand();
t[tot].si=1;
t[tot].l=t[tot].r=t[tot].rev=0;
return tot;
}
int copy_node(int p)
{
int now=++tot;
t[now]=t[p];
return now;
}
void pushup(int p)
{
t[p].si=t[t[p].l].si+t[t[p].r].si+1;
t[p].sum=t[t[p].l].sum+t[t[p].r].sum+t[p].val;
}
void pushdown(int p)
{
if(!t[p].rev) return ;
if(t[p].l) t[p].l=copy_node(t[p].l),t[t[p].l].rev^=1;
if(t[p].r) t[p].r=copy_node(t[p].r),t[t[p].r].rev^=1;
swap(t[p].l,t[p].r);
t[p].rev=0;
}
int _merge(int x,int y)
{
if(!x||!y) return x|y;
if(t[x].dat<t[y].dat)
{
pushdown(x);
t[x].r=_merge(t[x].r,y);
pushup(x);
return x;
}
else
{
pushdown(y);
t[y].l=_merge(x,t[y].l);
pushup(y);
return y;
}
}
pr split(int x,int k)
{
if(!x) return pr(0,0);
pr y;
pushdown(x);
if(t[t[x].l].si<k)
{
int p=copy_node(x);
y=split(t[p].r,k-t[t[x].l].si-1);
t[p].r=y.first;
pushup(p);
y.first=p;
}
else
{
int p=copy_node(x);
y=split(t[p].l,k);
t[p].l=y.second;
pushup(p);
y.second=p;
}
return y;
}
void del(int &root,int k)
{
pr x=split(root,k-1),y=split(x.second,1);
root=_merge(x.first,y.second);
}
void _insert(int &root,int k,int val)
{
pr x=split(root,k);
int rt=newnode(val);
root=_merge(_merge(x.first,rt),x.second);
}
void _revserve(int &root,int l,int r)
{
pr x=split(root,l-1),y=split(x.second,r-l+1);
t[y.first].rev^=1;
root=_merge(_merge(x.first,y.first),y.second);
}
ll ask(int &root,int l,int r)
{
pr x=split(root,l-1),y=split(x.second,r-l+1);
ll ans=t[y.first].sum;
root=_merge(_merge(x.first,y.first),y.second);
return ans;
}
int main(void)
{
int n;
int vi,pos,x,y;
scanf("%d",&n);
ll last=0;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&vi,&pos);
root[i]=root[vi];
if(pos==1)
{
scanf("%d%d",&x,&y);
_insert(root[i],x^last,y^last);
}
else if(pos==2)
{
scanf("%d",&x);
del(root[i],x^last);
}
else if(pos==3)
{
scanf("%d%d",&x,&y);
_revserve(root[i],x^last,y^last);
}
else
{
scanf("%d%d",&x,&y);
last=ask(root[i],x^last,y^last);
printf("%lld\n",last);
}
}
return 0;
}