给出一个可重集 a a a(编号为 1),它支持以下操作:
0 p x y:将可重集 p p p 中大于等于 x x x 且小于等于 y y y 的值放入一个新的可重集中(新可重集编号为从 2 2 2 开始的正整数,是上一次产生的新可重集的编号 + 1 +1 +1)。
1 p t:将可重集 t t t 中的数放入可重集 p p p,且清空可重集 t t t(数据保证在此后的操作中不会出现可重集 t t t)。
2 p x q:在 p p p 这个可重集中加入 x x x 个数字 q q q。
3 p x y:查询可重集 p p p 中大于等于 x x x 且小于等于 y y y 的值的个数。
4 p k:查询在 p p p 这个可重集中第 k k k 小的数,不存在时输出 − 1 -1 −1。
传送门
线段树分裂模板
关于线段树分裂
分裂是 线段树合并的逆操作
分裂的形式各异,主要思想就是
原树删元素,新树加元素,原树删了多少,新树就要有多少,这样才是分裂
这里通常维护的是权值线段树
比如将区间
l
∼
r
l \sim r
l∼r 的数据转移,假设这个操作是永久性的(将这些数据真实转移过去)
那么可以类比线段树查询
对于一个完全囊括的区间,可以直接将区间节点给新的树,表示将节点完全转移到新树上,区间再更新值
又比如说,将前
K
K
K小的数据分裂到新树
对于左边的某个区间,如果它的个数小于当前的
K
K
K,则直接将节点交换到新的树上,如果某个区间只有一部分,则在原树的区间节点上减少个数,在新的树上增加个数,达到分裂转移的目的
给个题目,其在线解法需要使用线段树分裂,离线做法也有解释,是一个比较套路实用的做法
P2824 【[HEOI2016/TJOI2016]排序】【线段树分裂】+【珂朵莉树】在线做法
关键代码
// 将 p 树上区间 l~r的数据,全部转移到 q 树上
void split(int p, int& q, int l, int r, int x, int y) {
if (!p) return; // 原树上没有数据了,直接返回
q = ++indx; // 建立一个新树的节点
if (x <= l && r <= y) {
// 刚好囊括区间,直接将这个区间给到新树上
swap(tr[p], tr[q]);
return;
}
int mid = l + ((r-l)>>1);
// 左右递归分裂
if (x <= mid) split(tr[p].l, tr[q].l, l, mid, x, y);
if (y > mid) split(tr[p].r, tr[q].r, mid+1, r, x, y);
// 跟新分裂后的节点信息
push_up(p);
push_up(q);
}
代码
//P5494
/*
@Author: YooQ
*/
#include <bits/stdc++.h>
using namespace std;
#define sc scanf
#define pr printf
#define ll long long
#define int long long
#define FILE_OUT freopen("out", "w", stdout);
#define FILE_IN freopen("P5494_1.in", "r", stdin);
#define debug(x) cout << #x << ": " << x << "\n";
#define AC 0
#define WA 1
#define INF 0x3f3f3f3f
const ll MAX_N = 1e6+5;
const ll MOD = 1e9+7;
int N, M, K;
int arr[MAX_N];
struct Tr {
int k, l, r;
}tr[MAX_N<<4];
int indx = 0;
int root[MAX_N];
void push_up(int rt) {
tr[rt].k = tr[tr[rt].l].k + tr[tr[rt].r].k;
}
void update(int& rt, int l, int r, int x, int k) {
if (!rt) rt = ++indx;
if (l == r) {
tr[rt].k += k;
return;
}
int mid = l + ((r-l)>>1);
if (x <= mid) update(tr[rt].l, l, mid, x, k);
if (x > mid) update(tr[rt].r, mid+1, r, x, k);
push_up(rt);
}
int query(int rt, int l, int r, int x, int y) {
if (!rt) return 0;
if (x <= l && r <= y) {
return tr[rt].k;
}
int mid = l + ((r-l)>>1);
if (y <= mid) return query(tr[rt].l, l, mid, x, y);
if (x > mid) return query(tr[rt].r, mid+1, r, x, y);
return query(tr[rt].l, l, mid, x, y) + query(tr[rt].r, mid+1, r, x, y);
}
int kth(int rt, int l, int r, int k) {
if (!rt || tr[rt].k < k) return -1;
if (l == r) {
return l;
}
int mid = l + ((r-l)>>1);
int lsum = tr[tr[rt].l].k;
if (lsum >= k) return kth(tr[rt].l, l, mid, k);
return kth(tr[rt].r, mid+1, r, k-lsum);
}
int merge(int x, int y, int l, int r) {
if (!x || !y) return x | y;
if (l == r) {
tr[x].k += tr[y].k;
return x;
}
int mid = l + ((r-l)>>1);
tr[x].l = merge(tr[x].l, tr[y].l, l, mid);
tr[x].r = merge(tr[x].r, tr[y].r, mid+1, r);
push_up(x);
return x;
}
void split(int p, int& q, int l, int r, int x, int y) {
if (!p) return;
q = ++indx;
if (x <= l && r <= y) {
swap(tr[p], tr[q]);
return;
}
int mid = l + ((r-l)>>1);
if (x <= mid) split(tr[p].l, tr[q].l, l, mid, x, y);
if (y > mid) split(tr[p].r, tr[q].r, mid+1, r, x, y);
push_up(p);
push_up(q);
}
void solve(){
sc("%lld%lld", &N, &M);
int opt, p, l, r, k, x, q, y, t;
for (int i = 1; i <= N; ++i) {
sc("%lld", &k);
update(root[1], 1, N, i, k);
}
int rcnt = 1;
for (int i = 1; i <= M; ++i) {
sc("%lld%lld", &opt, &p);
if (opt == 0) {
sc("%lld%lld", &x, &y);
split(root[p], root[++rcnt], 1, N, x, y);
} else if (opt == 1) {
sc("%lld", &t);
root[p] = merge(root[p], root[t], 1, N);
} else if (opt == 2) {
sc("%lld%lld", &x, &q);
update(root[p], 1, N, q, x);
} else if (opt == 3) {
sc("%lld%lld", &x, &y);
pr("%lld\n", query(root[p], 1, N, x, y));
} else {
sc("%lld", &k);
pr("%lld\n", kth(root[p], 1, N, k));
}
}
}
signed main()
{
#ifndef ONLINE_JUDGE
FILE_IN
FILE_OUT
#endif
int T = 1;//cin >> T;
while (T--) solve();
return AC;
}