洛谷P2161 [SHOI2009] 会场预约 题解 FHQ Treap

题目链接:https://www.luogu.com.cn/problem/P2161

题目大意:

你需要维护一个在数轴上的线段的集合 \(S\),支持两种操作:

A l r 表示将 \(S\) 中所有与线段 \([l,r]\) 相交的线段删去,并将 \([l,r]\) 加入 \(S\) 中。

B 查询 \(S\) 中的元素数量。

对于 A 操作,每次还需输出删掉的元素个数。

解题思路:

这题有个比较重要的点:就是 不存在重叠的区间 (来自 henry_y大佬的博客

需要双关键字,分割的时候需要开两个函数(一个根据第一个关键字分割,另一个根据第二个关键字分隔)

示例程序:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
mt19937 rng(time(0));

struct Node {
	int ls, rs, key1, key2, pri, sz;
	Node() {}
	Node(int _key1, int _key2) { ls = rs = 0; key1 = _key1; key2 = _key2; pri = rng(); sz = 1; }
} tr[maxn];
int rt, idx;

void push_up(int u) {
	int ls = tr[u].ls, rs = tr[u].rs;
	tr[u].sz = tr[ls].sz + tr[rs].sz + 1;
}

void split1(int u, int x, int &L, int &R) {
	if (!u) {
		L = R = 0;
		return;
	}
	if (tr[u].key1 <= x) {
		L = u;
		split1(tr[u].rs, x, tr[u].rs, R);
	}
	else {
		R = u;
		split1(tr[u].ls, x, L, tr[u].ls);
	}
	push_up(u);
}

void split2(int u, int x, int &L, int &R) {
	if (!u) {
		L = R = 0;
		return;
	}
	if (tr[u].key2 <= x) {
		L = u;
		split2(tr[u].rs, x, tr[u].rs, R);
	}
	else {
		R = u;
		split2(tr[u].ls, x, L, tr[u].ls);
	}
	push_up(u);
}

int merge(int L, int R) {
	if (!L || !R) return L + R;
	if (tr[L].pri > tr[R].pri) {
		tr[L].rs = merge(tr[L].rs, R);
		push_up(L);
		return L;
	}
	else {
		tr[R].ls = merge(L, tr[R].ls);
		push_up(R);
		return R;
	}
}

int cal(int key1, int key2) {
    int L, p, R;
    split2(rt, key1-1, L, R);
    split1(R, key2, p, R);
    tr[++idx] = Node(key1, key2);
    rt = merge(merge(L, idx), R);
    return tr[p].sz;
}

int n, key1, key2;
char op[5];

int main() {
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
        scanf("%s", op);
        if (op[0] == 'A') {
            scanf("%d%d", &key1, &key2);
            printf("%d\n", cal(key1, key2));
        }
        else {
            printf("%d\n", tr[rt].sz);
        }
	}
	return 0;
}
posted @ 2025-04-15 18:34  quanjun  阅读(22)  评论(0)    收藏  举报