20250730 ACM

通过极限拼分拿到了210,但是却只A了一题,自卑


图片
图片
图片

A

B

图片

开三个堆,每次如果堆顶有同一个人或者不合法就把那个人拉黑然后pop,直到找到第一组合法的,就是解。


C

图片

线段树,每个点开一个堆。存一个maxn

对于插入,找到所有覆盖的最浅的插入。

对于查询,直接将maxn取max即可。

对于删除,如果当前堆顶是要删除的元素,就pushdown。

pushdown有三种情况,肯定把当前结点的弹掉,然后如果只覆盖了半边,再把另一半插回去,否则递归。不会爆炸的,因为修改的是路径。


#include <bits/stdc++.h>
using namespace std;

// 使用线段树维护 n 个多重集合的插入、删除、查询操作
// 每个节点存储一个大顶堆,代表“懒插入”的值,以及子树的最大值 maxn

static const int MAXN = 200000;
struct Node {
	int l, r;                 // 区间 [l, r]
	int maxn;                 // 当前节点及其子树的最大值
	priority_queue<int> q;    // 存储完全覆盖该区间的插入值
} seg[4 * MAXN + 5];

int ls[4 * MAXN + 5], rs[4 * MAXN + 5];
int root = 0, nodeCnt = 0;

// 将值 k "插入" 到节点 x:往堆中 push,并更新 maxn
void apply(int x, int k) {
	seg[x].q.push(k);
	seg[x].maxn = max(seg[x].maxn, k);
}

// 上推:根据左右子节点和自身堆顶,更新 maxn
void pushUp(int x) {
	int lm = seg[ls[x]].maxn;
	int rm = seg[rs[x]].maxn;
	int qm = seg[x].q.empty() ? -1 : seg[x].q.top();
	seg[x].maxn = max({lm, rm, qm});
}

// 下推删除:将父节点被删除的 k 分发给子节点的堆中
void pushDown(int x, int delL, int delR, int k) {
	// 如果当前节点区间完全在删除区间内,停止分发
	if (delL <= seg[x].l && seg[x].r <= delR) return;
	int mid = (seg[x].l + seg[x].r) >> 1;
	// 三种情况:删除区间在右侧、左侧或跨中点
	if (mid < delL) {
		apply(ls[x], k);
		pushDown(rs[x], delL, delR, k);
	} else if (delR <= mid) {
		apply(rs[x], k);
		pushDown(ls[x], delL, delR, k);
	} else {
		pushDown(ls[x], delL, delR, k);
		pushDown(rs[x], delL, delR, k);
	}
	pushUp(x);
}

// 构建线段树,返回当前节点编号 x
void build(int l, int r, int &x) {
	x = ++nodeCnt;
	seg[x].l = l;
	seg[x].r = r;
	seg[x].maxn = -1;
	seg[x].q.push(-1); // 哨兵,保证堆不空
	if (l == r) return;
	int mid = (l + r) >> 1;
	build(l, mid, ls[x]);
	build(mid+1, r, rs[x]);
}

// 区间插入 k
void modify(int x, int L, int R, int k) {
	int l = seg[x].l, r = seg[x].r;
	if (L <= l && r <= R) {
		apply(x, k);
		return;
	}
	int mid = (l + r) >> 1;
	if (L <= mid)    modify(ls[x], L, R, k);
	if (R >  mid)    modify(rs[x], L, R, k);
	pushUp(x);
}

// 区间查询最大值
int query(int x, int L, int R) {
	int l = seg[x].l, r = seg[x].r;
	if (L <= l && r <= R) return seg[x].maxn;
	int ans = seg[x].q.top();
	int mid = (l + r) >> 1;
	if (L <= mid)    ans = max(ans, query(ls[x], L, R));
	if (R >  mid)    ans = max(ans, query(rs[x], L, R));
	return ans;
}

// 区间删除:删除每个集合中恰好一个值 k
void remove_k(int x, int L, int R, int k) {
	int l = seg[x].l, r = seg[x].r;
	// 若当前子树最大值都 < k,则不用继续
	if (L <= l && r <= R && seg[x].maxn < k) return;
	// 若当前节点堆顶正好是 k,先 pop,然后下推
	if (seg[x].q.top() == k) {
		seg[x].q.pop();
		pushDown(x, L, R, k);
		// 重新更新 maxn
		if (l == r) seg[x].maxn = seg[x].q.top();
		else        pushUp(x);
		return;
	}
	// 否则在子树中继续删除
	int mid = (l + r) >> 1;
	if (L <= mid)    remove_k(ls[x], L, R, k);
	if (R >  mid)    remove_k(rs[x], L, R, k);
	pushUp(x);
}

int main() {
	freopen("memory.in","r",stdin);
	freopen("memory.out","w",stdout); 
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	int n, m;
	cin >> n >> m;
	
	// 初始化线段树
	build(1, n, root);
	
	while (m--) {
		int op, l, r, k;
		cin >> op;
		if (op == 1) {
			// 操作1:区间插入 k
			cin >> l >> r >> k;
			modify(root, l, r, k);
		}
		else if (op == 2) {
			// 操作2:删除区间并集中的最大值
			cin >> l >> r;
			int T = query(root, l, r);
			if (T != -1) remove_k(root, l, r, T);
		}
		else if (op == 3) {
			// 操作3:查询区间并集的最大值
			cin >> l >> r;
			cout << query(root, l, r) << '\n';
		}
	}
	
	return 0;
}


D

E

F

G

否则再子树里递归。

posted @ 2025-07-30 21:16  Dreamers_Seve  阅读(10)  评论(1)    收藏  举报