线段树分裂

P5494 【模板】线段树分裂

题目链接P5494 【模板】线段树分裂 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

/*
给出一个可重集 aa(编号为 11),它支持以下操作:

0 p x y:将可重集 pp 中大于等于 xx 且小于等于 yy 的值放入一个新的可重集中
(新可重集编号为从 2 开始的正整数,是上一次产生的新可重集的编号+1)。
1 p t:将可重集 tt 中的数放入可重集 pp,且清空可重集 tt(数据保证在此后的操作中不会出现可重集 tt)。
2 p x q:在 pp 这个可重集中加入 xx 个数字 qq。
3 p x y:查询可重集 pp 中大于等于 xx 且小于等于 yy 的值的个数。
4 p k:查询在 pp 这个可重集中第 kk 小的数,不存在时输出 -1。
*/
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
const int maxn = 200010;
struct sgt_tree//线段树的分裂与合并
{
#define ls (son[p][0])
#define rs (son[p][1])
	int son[maxn << 5][2];//记录左右儿子编号
	ll val[maxn << 5];//权值
	int bac[maxn << 5], tot, cnt;//垃圾回收
	int addnode() { return cnt ? bac[cnt--] : ++tot; }
	void del(int p) {
		bac[++cnt] = p; son[p][0] = son[p][1] = val[p] = 0;
	}
	void modify(int& p, int l, int r, int pos, int v) {
		//单点修改
		if (!p)p = addnode();
		val[p] += v;
		if (l == r)return;
		int mid = (l + r) >> 1;
		if (pos <= mid)modify(ls, l, mid, pos, v);
		else modify(rs, mid + 1, r, pos, v);
	}
	ll query(int p, int l, int r, int ql, int qr) {
		//区间和查询
		if (l > qr || r < ql)return 0;
		if (ql <= l && r <= qr)return val[p];
		int mid = (l + r) >> 1;
		return query(ls, l, mid, ql, qr) + query(rs, mid + 1, r, ql, qr);
	}
	int kth(int p, int l, int r, int k) {
		//第k小查询
		if (l == r)return l;
		int mid = (l + r) >> 1;
		if (val[ls] >= k)return kth(ls, l, mid, k);
		else return kth(rs, mid + 1, r, k - val[ls]);
	}
	int merge(int x, int y)
	{
		//线段树的合并
		if (!x || !y)return x + y;
		val[x] += val[y];
		son[x][0] = merge(son[x][0], son[y][0]);
		son[x][1] = merge(son[x][1], son[y][1]);
		del(y);
		return x;
	}
	void split(int x, int& y, ll k)
	{
		//线段树分裂前k个数出来
		if (x == 0)return;
		y = addnode();
		ll v = val[son[x][0]];
		if (k > v)split(son[x][1], son[y][1], k - v);
		else swap(son[x][1], son[y][1]);
		if (k < v)split(son[x][0], son[y][0], k);
		val[y] = val[x] - k;
		val[x] = k;
		return;
	}
#undef ls
#undef rs
}sgt;
int rt[maxn], seq = 1;
int n, m;
int main()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++)
	{
		int x; scanf("%d", &x);
		sgt.modify(rt[1], 1, n, i, x);
	}
	for (int i = 1; i <= m; i++)
	{
		int op, x, y, z;
		scanf("%d", &op);
		if (op == 0)
		{
			scanf("%d%d%d", &x, &y, &z);
			ll k1 = sgt.query(rt[x], 1, n, 1, z), k2 = sgt.query(rt[x], 1, n, y, z);
			int tmp = 0;
			sgt.split(rt[x], rt[++seq], k1 - k2);
			sgt.split(rt[seq], tmp, k2);
			rt[x] = sgt.merge(rt[x], tmp);
		}
		else if (op == 1)
		{
			scanf("%d%d", &x, &y);
			rt[x] = sgt.merge(rt[x], rt[y]);
		}
		else if (op == 2)
		{
			scanf("%d%d%d", &x, &y, &z);
			sgt.modify(rt[x], 1, n, z, y);
		}
		else if (op == 3)
		{
			scanf("%d%d%d", &x, &y, &z);

			printf("%lld\n", sgt.query(rt[x], 1, n, y, z));
		}
		else if (op == 4)
		{
			scanf("%d%d", &x, &y);

			if (sgt.val[rt[x]] < y)puts("-1");
			else printf("%d\n", sgt.kth(rt[x], 1, n, y));
		}

	}
	return 0;
}
posted @ 2021-10-05 23:57  warmhearthhh  阅读(54)  评论(0)    收藏  举报