P5494
题目描述
题解
乍一眼看,平衡树啊!于是我直接敲了个FHQ上去,结果90分,至今没有调出来qwq
我们还是先讲讲FHQ的做法:
其实唯一需要讲的就是操作
1
1
1(其他操作都跟普通平衡树差不多,也比较好推)
对于操作1,其实就是暴力拆,然后合并(因为
m
e
r
g
e
(
a
,
b
merge(a,b
merge(a,b)要求
a
,
b
a,b
a,b两个子树满足
a
a
a里的权值都小于
b
b
b中的权值,所以不能直接合并)
那么我们再来看看另外一种做法–线段树合并与分裂
其实主要需要讲的就是合并和分裂操作,其他都与普通的线段树操作一样(当然需要用到动态开点)
对于合并
m
e
r
g
e
merge
merge操作:
考虑合并
x
,
y
x,y
x,y节点(默认新节点就是
x
x
x,当然有的题必须新开一个节点作为合并后的节点,因题而异)
我们直接将
y
y
y中维护的信息加到
x
x
x上
然后递归操作:
t
r
[
x
]
.
l
=
m
e
r
g
e
(
t
r
[
x
]
.
l
,
t
r
[
y
]
.
l
)
;
tr[x].l=merge(tr[x].l,tr[y].l);
tr[x].l=merge(tr[x].l,tr[y].l);
t
r
[
x
]
.
r
=
m
e
r
g
e
(
t
r
[
x
]
.
r
,
t
r
[
y
]
.
r
)
;
tr[x].r=merge(tr[x].r,tr[y].r);
tr[x].r=merge(tr[x].r,tr[y].r);
然后将y节点回收(回收的意思就是把这个节点清空然后留给以后新建节点用,可以节省空间复杂度)
对于分裂
s
p
l
i
t
split
split操作
考虑将以
k
k
k为根节点的线段树分为两棵,按照某种标准分裂,此题是按照
n
u
m
num
num值分裂(也就是权值的个数),一棵仍然以
k
k
k为根节点,另一棵以
y
y
y为根节点
显然
y
y
y原来是不存在的,因此我们对他新建,如果
l
s
.
n
u
m
<
n
u
m
ls.num<num
ls.num<num那么左子树仍然是
k
k
k的,递归右子树下去分裂即可,否则右子树一定是
y
y
y的,因此我们先给
y
y
y的右子树赋值,再递归左子树下去分裂即可,代码如下:
void split(int k,int &y,int num){
y=build();$
if(num>tr[tr[k].l].num) split(tr[k].r,tr[y].r,num-tr[tr[k].l].num);
else swap(tr[k].r,tr[y].r);
if(num<tr[tr[k].l].num) split(tr[k].l,tr[y].l,num);
tr[y].num=tr[k].num-num,tr[k].num=num;
}
代码 无旋Treap
#include<bits/stdc++.h>
#define int long long
#define M 700009
using namespace std;
int read(){
int f=1,re=0;char ch;
for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
if(ch=='-'){f=-1,ch=getchar();}
for(;isdigit(ch);ch=getchar()) re=(re<<3)+(re<<1)+ch-'0';
return re*f;
}
int n,m,tot,rt[M],cnt=1;
const int mod=1e9+7;
struct tree{int l,r,num,siz,pos,val;}tr[M];
void pushup(int k){tr[k].siz=tr[tr[k].l].siz+tr[tr[k].r].siz+tr[k].num;}
int build(int val,int num){
tr[++tot].val=val,tr[tot].siz=tr[tot].num=num,tr[tot].pos=rand();
return tot;
}
int merge(int x,int y){
int p;
if(!x||!y) return x+y;
if(tr[x].pos<tr[y].pos) p=x,tr[p].r=merge(tr[p].r,y);
else p=y,tr[p].l=merge(x,tr[p].l);
pushup(p);return p;
}
void split(int x,int &a,int &b,int val){
if(!x){a=b=0;return;}
if(tr[x].val<=val) a=x,split(tr[a].r,tr[a].r,b,val);
else b=x,split(tr[b].l,a,tr[b].l,val);
pushup(x);
}
int kth(int k,int val){
if(val>tr[k].siz) return -1;
if(tr[tr[k].l].siz+tr[k].num<val) return kth(tr[k].r,val-tr[tr[k].l].siz-tr[k].num);
if(tr[tr[k].l].siz>=val) return kth(tr[k].l,val);
return tr[k].val;
}
void insert(int &k,int val,int num){
int x,y,z;
split(k,x,y,val);
split(x,x,z,val-1);
if(tr[z].val==val) tr[z].num+=num,tr[z].siz+=num;
else z=build(val,num);
k=merge(merge(x,z),y);
}
void dfs(int k,int &y){
if(tr[k].l) dfs(tr[k].l,y);
insert(y,tr[k].val,tr[k].num);
if(tr[k].r) dfs(tr[k].r,y);
}
signed main(){
n=read(),m=read();
for(int i=1;i<=n;i++){
int x=read();
rt[1]=merge(rt[1],build(i,x));
}
for(int i=1;i<=m;i++){
int opt=read(),p=read(),x,y,z,w,v;
if(opt==0) x=read(),y=read(),split(rt[p],z,rt[++cnt],x-1),split(rt[cnt],rt[cnt],w,y),rt[p]=merge(z,w);
if(opt==1) x=read(),dfs(rt[x],rt[p]);
if(opt==2) x=read(),y=read(),insert(rt[p],y,x);
if(opt==3) x=read(),y=read(),split(rt[p],z,w,x-1),split(w,w,v,y),printf("%lld\n",tr[w].siz),rt[p]=merge(z,merge(w,v));
if(opt==4) x=read(),printf("%lld\n",kth(rt[p],x));
}return 0;
}
代码 线段树合并与分裂
#include<bits/stdc++.h>
#define int long long
#define M 100009
using namespace std;
int read(){
int f=1,re=0;char ch;
for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
if(ch=='-'){f=-1,ch=getchar();}
for(;isdigit(ch);ch=getchar()) re=(re<<3)+(re<<1)+ch-'0';
return re*f;
}
int n,m,num1,tot,cnt=1,rec[M],rt[M];
struct tree{int l,r,num;}tr[M*32];
int build(){
if(num1) return rec[num1--];
else return ++tot;
}
void recyc(int x){rec[++num1]=x,tr[x].l=tr[x].r=tr[x].num=0;}
void update(int &k,int l,int r,int pos,int val){
if(!k) k=build();tr[k].num+=val;
if(l==r) return;
int mid=(l+r)>>1;
if(pos<=mid) update(tr[k].l,l,mid,pos,val);
else update(tr[k].r,mid+1,r,pos,val);
}
int query(int k,int l,int r,int ql,int qr){
if(l>=ql&&r<=qr) return tr[k].num;
int mid=(l+r)>>1,ret=0;
if(ql<=mid) ret+=query(tr[k].l,l,mid,ql,qr);
if(qr>mid) ret+=query(tr[k].r,mid+1,r,ql,qr);
return ret;
}
void split(int k,int &y,int num){
y=build();
if(num>tr[tr[k].l].num) split(tr[k].r,tr[y].r,num-tr[tr[k].l].num);
else swap(tr[k].r,tr[y].r);
if(num<tr[tr[k].l].num) split(tr[k].l,tr[y].l,num);
tr[y].num=tr[k].num-num,tr[k].num=num;
}
int merge(int x,int y){
if(!x||!y) return x+y;
tr[x].num+=tr[y].num;
tr[x].l=merge(tr[x].l,tr[y].l);
tr[x].r=merge(tr[x].r,tr[y].r);
recyc(y);return x;
}
int kth(int k,int l,int r,int num){
if(tr[k].num<num) return -1;
if(l==r) return l;
int mid=(l+r)>>1;
if(tr[tr[k].l].num<num) return kth(tr[k].r,mid+1,r,num-tr[tr[k].l].num);
else return kth(tr[k].l,l,mid,num);
}
signed main(){
n=read(),m=read();
for(int i=1;i<=n;i++){
int x=read();
update(rt[1],1,n,i,x);
}
for(int i=1;i<=m;i++){
int opt=read(),p=read(),x,y,z,w,v;
if(opt==0) x=read(),y=read(),z=query(rt[p],1,n,1,y),w=query(rt[p],1,n,x,y),split(rt[p],rt[++cnt],z-w),split(rt[cnt],v,w),rt[p]=merge(rt[p],v);
if(opt==1) x=read(),rt[p]=merge(rt[p],rt[x]);
if(opt==2) x=read(),y=read(),update(rt[p],1,n,y,x);
if(opt==3) x=read(),y=read(),printf("%lld\n",query(rt[p],1,n,x,y));
if(opt==4) x=read(),printf("%lld\n",kth(rt[p],1,n,x));
}return 0;
}