1 条题解
-
0
自动搬运
来自洛谷,原作者为

封禁用户
None搬运于
2025-08-24 22:58:45,当前版本为作者最后更新于2025-08-08 18:54:35,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
P10513 括号
我们考虑使用线段树实现,对于每个区间,可以维护区间内的最大子段和。
为了进行合并我们还需维护剩余的左/右括号个数,在合并时统计跨块的括号对。
具体来讲,左区间剩余的左括号可以与右区间剩余的右括号结合,数量为两者的 值。
Node merge(Node x, Node y) { int minn = min(x.lv, y.rv); return{x.v + y.v + minn,x.lv + y.lv - minn,x.rv + y.rv - minn}; }其中
v是当前区间的答案,lv和rv分别是剩余的左/右括号个数。对于区间取反操作,似乎不好直接维护,取反会导致我们维护好的括号全部失配,难以计算。
这里其实有个小 trick,我们可以维护两棵线段树,分别维护正常字符串和取反后的字符串,我们在更新时直接交换两颗树的节点即可。
可以发现,这两颗树只有
v,lv,rv是不一样的,所以我们可以把这三个变量封成结构体,将它们缩至一棵树,以降低编程复杂度。时间复杂度 。
代码实现
#include <bits/stdc++.h> using namespace std; const int N = 5e5 + 5; struct Node { int v, lv, rv; // v: 匹配对数,lv: 左括号剩余,rv: 右括号剩余 }; Node a[N << 2], b[N << 2]; // a: 正常匹配,b: 反转后匹配 bool lazy[N << 2]; // 懒标记,表示是否翻转 char s[N]; // 输入字符串 // 合并两个节点信息 Node merge(Node x, Node y) { int minn = min(x.lv, y.rv); return{x.v + y.v + minn,x.lv + y.lv - minn,x.rv + y.rv - minn}; } // 构建线段树 void build(int l, int r, int rt) { if (l == r) { if (s[l] == '(') a[rt] = {0, 1, 0},b[rt] = {0, 0, 1}; else if (s[l] == ')') a[rt] = {0, 0, 1},b[rt] = {0, 1, 0}; else a[rt] = b[rt] = {0, 0, 0}; } else{ int mid = (l + r) >> 1; build(l, mid, rt << 1); build(mid + 1, r, rt << 1 | 1); a[rt] = merge(a[rt << 1], a[rt << 1 | 1]); b[rt] = merge(b[rt << 1], b[rt << 1 | 1]); } } // 下推懒标记 void pushdown(int rt) { if (lazy[rt]) { lazy[rt << 1] ^= 1; lazy[rt << 1 | 1] ^= 1; swap(a[rt << 1], b[rt << 1]); swap(a[rt << 1 | 1], b[rt << 1 | 1]); lazy[rt] = 0; } } // 区间翻转操作 void update(int L, int R, int l, int r, int rt) { if (R < l || r < L) return; if (L <= l && r <= R) { lazy[rt] ^= 1; swap(a[rt], b[rt]); return; } pushdown(rt); int mid = (l + r) >> 1; update(L, R, l, mid, rt << 1); update(L, R, mid + 1, r, rt << 1 | 1); a[rt] = merge(a[rt << 1], a[rt << 1 | 1]); b[rt] = merge(b[rt << 1], b[rt << 1 | 1]); } // 查询区间匹配对数 Node query(int L, int R, int l, int r, int rt) { if (R < l || r < L) return {0, 0, 0}; if (L <= l && r <= R) return a[rt]; pushdown(rt); int mid = (l + r) >> 1; return merge(query(L, R, l, mid, rt << 1), query(L, R, mid + 1, r, rt << 1 | 1)); } int n,q; int main() { cin >> n >> s + 1 >> q; build(1, n, 1); while (q--) { int op, x, y; cin >> op >> x >> y; if (op == 1) update(x, y, 1, n, 1); else cout << query(x, y, 1, n, 1).v << "\n"; } return 0; }
- 1
信息
- ID
- 9424
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者