线段树分而裂之
线段树分裂:
线段树分裂者,算法分治之精要也。其术若庖丁解牛,循理而析。初始,整树为一,犹混沌未分。及至查询区间,则自根始,逐层剖判:若节点全含所求,则裂之为二,左存原枝,右立新干;若仅涉半域,则下探子枝,如禹导江河,各循其道。分裂既成,新树旧木并存,犹宗族之分爨,然血脉相连。凡标记懒传、子息重构,皆合《九章》"少广"之术,此乃计算机之道通于古算之明证也。
简要题意:
给出一个可重集 \(a\)(编号为 \(1\)),它支持以下操作:
\(0\) \(p\) \(x\) \(y\) :将可重集 \(p\) 中大于等于 \(x\) 且小于等于 \(y\) 的值移动到一个新的可重集中(新可重集编号为从 \(2\) 开始的正整数,是上一次产生的新可重集的编号+1)。
\(1\) \(p\) \(t\):将可重集 \(t\) 中的数放入可重集 \(p\),且清空可重集 \(t\)(数据保证在此后的操作中不会出现可重集 \(t\))。
\(2\) \(p\) \(x\) \(q\) :在 \(p\) 这个可重集中加入 \(x\) 个数字 \(q\)。
\(3\) \(p\) \(x\) \(y\) :查询可重集 \(p\) 中大于等于 \(x\) 且小于等于 \(y\) 的值的个数。
\(4\) \(p\) \(k\) :查询在 \(p\) 这个可重集中第 \(k\) 小的数,不存在时输出 \(-1\) 。
思路:
点击查看
按排名分:
\(Code\):
#include<bits/stdc++.h>
#define int long long
const int N = 6e6+10;
using namespace std;
int T[N],ls[N],rs[N],cnt,rt[N],tot=1,n,m;
int add(int i,int l,int r,int x,int k) {
if(!i) i=++cnt;
T[i]+=k;
if(l==r) return i;
int mid=l+r>>1;
if(x<=mid) ls[i]=add(ls[i],l,mid,x,k);
else rs[i]=add(rs[i],mid+1,r,x,k);
return i;
}
int get_val(int i,int l,int r,int k) {
if(l==r) return l;
int mid=l+r>>1,siz=T[ls[i]];
if(siz>=k) return get_val(ls[i],l,mid,k);
else return get_val(rs[i],mid+1,r,k-siz);
}
int get_rank(int i,int l,int r,int x,int tag) {
if(l==r) return 1+(T[i]-1)*tag;
int mid=l+r>>1,siz=T[ls[i]];
if(x<=mid) return get_rank(ls[i],l,mid,x,tag);
else return get_rank(rs[i],mid+1,r,x,tag)+siz;
}
int merge(int x,int y,int l,int r) {
if(!x||!y) return x|y;
T[x]+=T[y];
if(l==r)return x;
int mid=l+r>>1;
ls[x]=merge(ls[x],ls[y],l,mid);
rs[x]=merge(rs[x],rs[y],mid+1,r);
return x;
}
int split(int x,int y,int k) {
y=++cnt;
if(!x) return y;
int siz=T[ls[x]],siz2=T[x];
if(siz>k) {
swap(rs[x],rs[y]);
ls[y]=split(ls[x],ls[y],k);
}
else if(siz==k)swap(rs[x],rs[y]);
else rs[y]=split(rs[x],rs[y],k-siz);
T[y]=T[x]-k;T[x]=k;
return y;
}
signed main() {
cin>>n>>m;
for(int i=1,x;i<=n;i++) {
cin>>x;
rt[tot]=add(rt[tot],1,n,i,x);
}
for(int i=1,opt;i<=m;i++) {
cin>>opt;
if(opt==0) {
int p,x,y,nwt=0,nwr=0;
cin>>p>>x>>y;
int rxk=get_rank(rt[p],1,n,x,0),ryk=get_rank(rt[p],1,n,y,1);
rt[++tot]=split(rt[p],rt[tot],rxk-1);
int lrt=0;lrt=split(rt[tot],lrt,ryk-rxk+1);
rt[p]=merge(rt[p],lrt,1,n);
}
else if(opt==1){
int x,y;
cin>>x>>y;
rt[x]=merge(rt[x],rt[y],1,n);
}
else if(opt==2) {
int p,q,x;
cin>>p>>x>>q;
add(rt[p],1,n,q,x);
}
else if(opt==3) {
int p,x,y,nwt=0,nwr=0;
cin>>p>>x>>y;
int rxk=get_rank(rt[p],1,n,x,0),ryk=get_rank(rt[p],1,n,y,1),ans=0;
cout<<ryk-rxk+1<<'\n';
}
else {
int p,k;
cin>>p>>k;
if(T[rt[p]]<k) puts("-1");
else cout<<get_val(rt[p],1,n,k)<<"\n";
}
}
return 0;
}

线段树分裂
浙公网安备 33010602011771号