线段树分裂

可重集 ( ex权值线段树是吧 )

题目描述

给出一个可重集 \(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

#include<bits/stdc++.h>
#define int long long
#define F(i,i0,n) for(int i=i0;i<=n;i++)
using namespace std;
inline int rd(){
    int f=0,x=0;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=1;ch=getchar();}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
    return f?-x:x;
}
const int N=2e6+5;
struct Tr{
    int ls,rs,ct;
}tr[N*4];
#define lp (tr[p].ls)
#define rp (tr[p].rs)
#define mid (l+r>>1)
int cnt=0,tot=0;
int rub[N*4];
void del(int p){
    tr[p].ls=tr[p].rs=tr[p].ct=0;
    rub[++tot]=p;
}
int newnode(){return tot?rub[tot--]:++cnt;}//垃圾桶体系,节省节点
int a[N],n,m;
void pushup(int p){tr[p].ct=tr[lp].ct+tr[rp].ct;}
void merge(int &x,int y,int l=1,int r=n){
    if(!x||!y){x|=y;return;}
    if(l==r){
        tr[x].ct+=tr[y].ct;
        return ; 
    } 
    merge(tr[x].ls,tr[y].ls,l,mid);
    merge(tr[x].rs,tr[y].rs,mid+1,r);
    del(y);
    pushup(x);
}
void split(int x,int &y,int k){
    if(!x)return ;
    y=newnode();
    int num=tr[tr[x].ls].ct;
    if(num<k)split(tr[x].rs,tr[y].rs,k-num);
    else swap(tr[x].rs,tr[y].rs);
    if(num>k) split(tr[x].ls,tr[y].ls,k);
    pushup(x);pushup(y);
}
void update(int &p,int pos,int k,int l=1,int r=n){
    if(!p)p=newnode();
    if(l==r){
        tr[p].ct+=k;
        return ;
    }
    if(pos<=mid)update(lp,pos,k,l,mid);
    else update(rp,pos,k,mid+1,r);
    pushup(p);
}
int query(int p,int nl,int nr,int l=1,int r=n){
    if(!p)return 0;
    if(nl==l&&r==nr){
        return tr[p].ct;
    }
    if(nr<=mid)return query(lp,nl,nr,l,mid);
    else if(mid<nl)return query(rp,nl,nr,mid+1,r);
    else return query(lp,nl,mid,l,mid)+query(rp,mid+1,nr,mid+1,r);
}
int kth(int p,int k,int l=1,int r=n){
    if(l==r)return l;
    if(tr[lp].ct>=k)return kth(lp,k,l,mid);
    else return kth(rp,k-tr[lp].ct,mid+1,r);
}
#undef lp
#undef rp
#undef mid
int rt[N];
signed main(){
    n=rd(),m=rd();
    F(i,1,n){
        int x=rd();
        update(rt[1],i,x);
    }
    int id=1;
    while(m--){
        int op=rd();
        if(!op){
            int p=rd(),x=rd(),y=rd();++id;
            int k1=query(rt[p],1,y),k2=query(rt[p],x,y),tmp;
            split(rt[p],rt[id],k1-k2);
            split(rt[id],tmp,k2);
            merge(rt[p],tmp);   
        }
        else if(op==1){
            int p=rd(),t=rd();
            merge(rt[p],rt[t]);
        }
        else if(op==2){
            int p=rd(),x=rd(),q=rd();
            update(rt[p],q,x);
        }
        else if(op==3){
            int p=rd(),x=rd(),y=rd();
            cout<<query(rt[p],x,y)<<'\n';
        }
        else {
            int p=rd(),k=rd();
            if(tr[rt[p]].ct<k)cout<<-1<<'\n';
            else cout<<kth(rt[p],k)<<'\n';
        }
    }
    return 0;
}
posted @ 2023-09-09 17:07  ussumer  阅读(27)  评论(0)    收藏  举报