题意:给出一个可重集 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>
using namespace std;
typedef long long LL;
const int N = 6e5 + 10;
int n,m;
struct Tree{
int lson,rson;
LL sum;
}tr[4 * N];
int cnt;
int idx;
int rubbish[4 * N];
int a[N];
void Del(int &p){
tr[p].lson = tr[p].rson = tr[p].sum = 0;
rubbish[++ idx] = p;
p = 0;
}
int New(){
if(idx) return rubbish[idx --];
return ++ cnt;
}
void pushup(int u){
tr[u].sum = tr[tr[u].lson].sum + tr[tr[u].rson].sum;
}
void build(int &p,int l = 1,int r = N){
if(!p) p = New();
if(l == r){
tr[p].sum = a[l];
return;
}
int mid = l + r >> 1;
build(tr[p].lson,l,mid),build(tr[p].rson,mid + 1,r);
pushup(p);
}
void merge(int &p1,int &p2,int l = 1,int r = N){
if(!p1 || !p2){
p1 += p2;
return;
}
if(l == r){
tr[p1].sum += tr[p2].sum;
Del(p2);
return;
}
int mid = l + r >> 1;
merge(tr[p1].lson,tr[p2].lson,l,mid);
merge(tr[p1].rson,tr[p2].rson,mid + 1,r);
Del(p2);
pushup(p1);
}
void split(int &p1,int &p2,int ml,int mr,int l = 1,int r = N){//将p1中ml到mr部分分裂到p2中
if(r < ml || mr < l) return;
if(!p1) return;
if(l >= ml && r <= mr){
p2 = p1;
p1 = 0;
return;
}
if(!p2) p2 = New();
int mid = l + r >> 1;
split(tr[p1].lson,tr[p2].lson,ml,mr,l,mid);
split(tr[p1].rson,tr[p2].rson,ml,mr,mid + 1,r);
pushup(p1),pushup(p2);
}
void update(int &p,int x,int v,int l = 1,int r = N){
if(x < l || x > r) return;
if(!p) p = New();
if(l == r){
tr[p].sum += v;
return;
}
int mid = l + r >> 1;
update(tr[p].lson,x,v,l,mid);
update(tr[p].rson,x,v,mid + 1,r);
pushup(p);
}
LL querySum(int p,int ql,int qr,int l = 1,int r = N){
if(qr < l || r < ql) return 0;
if(!p) return 0;
if(l >= ql && r <= qr)
return tr[p].sum;
int mid = l + r >> 1;
return querySum(tr[p].lson,ql,qr,l,mid) + querySum(tr[p].rson,ql,qr,mid + 1,r);
}
int queryKth(int p,LL k,int l = 1,int r = N){
if(k <= 0) return -1;
if(l == r) return l;
int mid = l + r >> 1;
if(tr[tr[p].lson].sum >= k) return queryKth(tr[p].lson,k,l,mid);
return queryKth(tr[p].rson,k - tr[tr[p].lson].sum,mid + 1,r);
}
int root[N];
int cntTree = 1;
int main(){
cin >> n >> m;
for(int i = 1;i <= n;i ++) cin >> a[i];
build(root[1]);
for(int i = 1;i <= m;i ++){
//cout << "#" << querySum(root[2],1,n) << endl;
int op,p,x,y,q,k,t;
cin >> op;
if(op == 0){
cin >> p >> x >> y;
split(root[p],root[++ cntTree],x,y);
}
else if(op == 1){
cin >> p >> t;
merge(root[p],root[t]);
}
else if(op == 2){
cin >> p >> x >> q;
update(root[p],q,x);
}
else if(op == 3){
cin >> p >> x >> y;
cout << querySum(root[p],x,y) << endl;
}
else{
cin >> p >> k;
int kth;
if(querySum(root[p],1,n) < k) kth = -1;
else kth = queryKth(root[p],k);
cout << kth << endl;
}
}
return 0;
}
orz sto