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
否则再子树里递归。