线段树题解
A.【例题1】求区间和
单修区查,直接线段树即可。
当然也可以树状数组+差分。
Code
#include <bits/stdc++.h>
#define ls u << 1
#define rs u << 1 | 1
#define int long long
using namespace std;
const int N = 1e5 + 10;
int n, q, a[N];
struct node{
int val;
int l, r;
int lzy;
}t[N << 2];
void pushup(int u) {
t[u].val = t[ls].val + t[rs].val;
}
void maketag(int u, int x) {
t[u].lzy += x;
t[u].val += (t[u].r - t[u].l + 1) * x;
}
void pushdown(int u) {
if (t[u].lzy) {
maketag(ls, t[u].lzy);
maketag(rs, t[u].lzy);
t[u].lzy = 0;
}
}
void build(int u, int l, int r) {
t[u].val = t[u].lzy = 0;
t[u].l = l, t[u].r = r;
if (l == r) {
t[u].val = a[l];
return ;
}
int M = (l + r) >> 1;
build(ls, l, M);
build(rs, M + 1, r);
pushup(u);
}
void modify(int u, int l, int r, int k) {
if (l <= t[u].l && t[u].r <= r) maketag(u, k);
else {
pushdown(u);
int M = (t[u].l + t[u].r) >> 1;
if (l <= M) modify(ls, l, r, k);
if (r > M) modify(rs, l, r, k);
pushup(u);
}
}
int query(int u, int l, int r) {
if (l <= t[u].l && t[u].r <= r) return t[u].val;
pushdown(u);
int M = (t[u].l + t[u].r) >> 1, res = 0;
if (l <= M) res += query(ls, l, r);
if (r > M) res += query(rs, l, r);
return res;
}
signed main() {
scanf("%lld%lld", &n, &q);
for (int i = 1; i <= n; i++) {
a[i] = 0;
}
build(1, 1, n);
while (q--) {
int op, l, r;
scanf("%lld%lld%lld", &op, &l, &r);
if (op == 0) {
modify(1, l, l, r);
} else {
printf("%lld\n", query(1, l, r));
}
}
return 0;
}
B.【例题2】区间查改
区修区查板子,当然依旧可以树状数组。
Code
#include <bits/stdc++.h>
#define ls u << 1
#define rs u << 1 | 1
#define int long long
using namespace std;
template <typename T = int>
T rd() {
T s = 0;
int f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while ('0' <= ch && ch <= '9') {
s = (s << 3) + (s << 1) + ch - 48;
ch = getchar();
}
return s * f;
}
const int N = 1e6 + 10;
int n, q, a[N];
struct node{
int val;
int l, r;
int lzy;
}t[N << 2];
void pushup(int u) {
t[u].val = t[ls].val + t[rs].val;
}
void maketag(int u, int x) {
t[u].lzy += x;
t[u].val += (t[u].r - t[u].l + 1) * x;
}
void pushdown(int u) {
if (t[u].lzy) {
maketag(ls, t[u].lzy);
maketag(rs, t[u].lzy);
t[u].lzy = 0;
}
}
void build(int u, int l, int r) {
t[u].val = t[u].lzy = 0;
t[u].l = l, t[u].r = r;
if (l == r) {
t[u].val = a[l];
return ;
}
int M = (l + r) >> 1;
build(ls, l, M);
build(rs, M + 1, r);
pushup(u);
}
void modify(int u, int l, int r, int k) {
if (l <= t[u].l && t[u].r <= r) maketag(u, k);
else {
pushdown(u);
int M = (t[u].l + t[u].r) >> 1;
if (l <= M) modify(ls, l, r, k);
if (r > M) modify(rs, l, r, k);
pushup(u);
}
}
int query(int u, int l, int r) {
if (l <= t[u].l && t[u].r <= r) return t[u].val;
pushdown(u);
int M = (t[u].l + t[u].r) >> 1, res = 0;
if (l <= M) res += query(ls, l, r);
if (r > M) res += query(rs, l, r);
return res;
}
signed main() {
n = rd(), q = rd();
for (int i = 1; i <= n; i++) {
a[i] = rd();
}
build(1, 1, n);
while (q--) {
int op = rd(), l = rd(), r = rd();
if (op == 1) {
int k = rd();
modify(1, l, r, k);
} else {
printf("%lld\n", query(1, l, r));
}
}
return 0;
}
C.【例题3】公园遛狗
维护四个值:区间和,区间中最大子段和,最大前缀和,最大后缀和。
接下来我们重点考虑合并操作(即 pushup 操作)。
区间和的合并只需要将两个区间的区间和加起来即可。
区间最大前缀和:
容易证明这样一定是最大的,证明如下:
我们考虑区间前缀和右端点的位置。
如果在左区间内,那么显然取左区间最大前缀和是最优的。
如果在右区间内,那么我们必然选取左区间的全部,这部分权值是定值,那么在右区间的部分选取最大前缀和也是最优的。
证毕。
区间最大后缀和同理。
区间中最大子段和:
首先先选取
接下来,如果左区间的最大后缀和或右区间的最大前缀和有一个小于
最后为两者的最大值。
合并操作代码:
//l是区间左端点,r是区间右端点
//mx是最大子段和,ml是最大前缀和,mr是最大后缀和
//val是区间和
inline node solve(node a, node b) {
node res;
res.l = a.l, res.r = b.r;
res.val = a.val + b.val;
if ((a.mr < 0) || (b.ml < 0)) {
res.mx = max(a.mr, b.ml);
} else {
res.mx = a.mr + b.ml;
}
res.mx = max(res.mx, a.mx);
res.mx = max(res.mx, b.mx);
res.ml = max(a.ml, a.val + b.ml);
res.mr = max(b.mr, b.val + a.mr);
return res;
}
好了,这题基本上就做完了,小细节是 query
函数返回值应该写一个 node
。
Code
#include <bits/stdc++.h>
#define ls u << 1
#define rs u << 1 | 1
#define int long long
using namespace std;
const int N = 1e6 + 10;
int n, q, a[N];
struct node{
int val;
int l, r;
int ml, mr;
int mx;
}t[N << 2];
inline int rd() {
int s = 0, f = 1;
char ch = getchar();
while ('0' > ch || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while ('0' <= ch && ch <= '9') {
s = (s << 3) + (s << 1) + ch - 48;
ch = getchar();
}
return s * f;
}
inline node solve(node a, node b) {
node res;
res.l = a.l, res.r = b.r;
res.val = a.val + b.val;
if ((a.mr < 0) || (b.ml < 0)) {
res.mx = max(a.mr, b.ml);
} else {
res.mx = a.mr + b.ml;
}
res.mx = max(res.mx, a.mx);
res.mx = max(res.mx, b.mx);
res.ml = max(a.ml, a.val + b.ml);
res.mr = max(b.mr, b.val + a.mr);
return res;
}
inline void pushup(const int u) {
node k = solve(t[ls], t[rs]);
t[u] = k;
}
void build(const int u, int l, int r) {
t[u].l = l, t[u].r = r;
if (l == r) {
t[u].val = t[u].ml = t[u].mr = t[u].mx = a[l];
return ;
}
int M = (l + r) >> 1;
build(ls, l, M);
build(rs, M + 1, r);
pushup(u);
}
node query(const int u, int l, int r) {
if (l <= t[u].l && r >= t[u].r) return t[u];
int M = (t[u].l + t[u].r) >> 1;
if (l <= M && r > M) {
node x = query(ls, l, r);
node y = query(rs, l, r);
return solve(x, y);
}
else if (l <= M) return query(ls, l, r);
else return query(rs, l, r);
}
void modify(const int u, int p, int k) {
if (t[u].l == t[u].r) {
t[u].val = t[u].ml = t[u].mr = t[u].mx = k;
return;
}
int M = (t[u].l + t[u].r) >> 1;
if (p <= M) modify(ls, p, k);
else modify(rs, p, k);
pushup(u);
}
signed main() {
n = rd(), q = rd();
for (int i = 1; i <= n; i++) {
a[i] = rd();
}
build(1, 1, n);
while (q--) {
int op = rd(), x = rd(), y = rd();
if (op == 1) {
if (x > y) swap(x, y);
node ans = query(1, x, y);
printf("%d\n", ans.mx);
} else {
modify(1, x, y);
}
}
return 0;
}
D.【例题4】维护序列
板子题,打两个懒标记即可。
Code
#include <bits/stdc++.h>
#define ls u * 2
#define rs u * 2 + 1
#define int long long
using namespace std;
const int N = 1e5 + 10;
struct node{
int val;
int l, r;
int lza, lzm;
}t[N << 2];
int n, q, P, a[N];
inline int read() {
int s = 0, f = 1;
char ch = getchar();
while ('0' > ch || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while ('0' <= ch && ch <= '9') {
s = (s << 3) + (s << 1) + ch - 48;
ch = getchar();
}
return s * f;
}
inline void pushup(const int u) {
t[u].val = (t[ls].val + t[rs].val) % P;
return ;
}
inline void maketag(const int u, int x, int type) {
if (type == 1) {
t[u].lza = (t[u].lza * x) % P;
t[u].lzm = (t[u].lzm * x) % P;
t[u].val = (t[u].val * x) % P;
} else {
t[u].lza = (t[u].lza + x) % P;
t[u].val = (t[u].val + (x * (t[u].r - t[u].l + 1) % P)) % P;
}
return ;
}
inline void pushdown(const int u) {
maketag(ls, t[u].lzm, 1);
maketag(rs, t[u].lzm, 1);
maketag(ls, t[u].lza, 2);
maketag(rs, t[u].lza, 2);
t[u].lza = 0, t[u].lzm = 1;
return ;
}
void build(const int u, int l, int r) {
t[u].l = l, t[u].r = r, t[u].lza = 0, t[u].lzm = 1;
if (l == r) {
t[u].val = a[l];
return ;
}
int M = (l + r) >> 1;
build(ls, l, M);
build(rs, M + 1, r);
pushup(u);
return ;
}
int query(const int u, int l, int r) {
if (l <= t[u].l && r >= t[u].r) return t[u].val;
pushdown(u);
int M = (t[u].l + t[u].r) >> 1, sum = 0;
if (l <= M) sum = (sum + query(ls, l, r)) % P;
if (r > M) sum = (sum + query(rs, l, r)) % P;
return sum % P;
}
void modify(const int u, int l, int r, int k, int type) {
if (l <= t[u].l && r >= t[u].r) maketag(u, k, type);
else {
pushdown(u);
int M = (t[u].l + t[u].r) >> 1;
if (l <= M) modify(ls, l, r, k, type);
if (r > M) modify(rs, l, r, k, type);
pushup(u);
}
return ;
}
signed main() {
n = read(), P = read();
for (int i = 1; i <= n; i++) a[i] = read();
build(1, 1, n);
q = read();
while (q--) {
int op, x, y, k;
op = read();
if (op == 1 || op == 2) {
x = read(), y = read(), k = read();
modify(1, x, y, k, op);
} else {
x = read(), y = read();
printf("%lld\n", query(1, x, y) % P);
}
}
return 0;
}
E.【例题5】字符串排序
定义函数
由于只有
具体的,第
考虑排序操作,这里只讨论升序排序,因为降序排序同理即可。
我们观察一个例子:对于 acababc
进行排序,结果是 aaabbcc
。这里,第 a
,第 b
,第 c
。注意到这里的
每次修改时,从小往大枚举字符,这样就可以保证升序。对于字符
这里,我们利用了两个性质:排序后相同的字符是连续的;排序操作不影响区间内字符的个数。
输出的时候,我们遍历时遍历到叶子节点输出即可。
时间复杂度
其实此题与这个题有着较为相似的思路,只不过后者将值域从
Code
#include <bits/stdc++.h>
#define ls u << 1
#define rs u << 1 | 1
using namespace std;
const int N = 1e5 + 10;
int n, m;
char a[N];
struct tree{
int l, r;
int val;
int lzy;
}t[27][N << 4];
void pushup(int u, int c) {
t[c][u].val = t[c][ls].val + t[c][rs].val;
}
void maketag(int u, int x, int c) {
//cout << c << ' ' << x << endl;
t[c][u].lzy = x;
t[c][u].val = x * (t[c][u].r - t[c][u].l + 1);
}
void pushdown(int u, int c) {
if (t[c][u].lzy != -1) {
maketag(ls, t[c][u].lzy, c);
maketag(rs, t[c][u].lzy, c);
t[c][u].lzy = -1;
}
}
void build(int u, int l, int r) {
for (int i = 0; i < 26; i++) {
t[i][u].l = l, t[i][u].r = r, t[i][u].lzy = -1, t[i][u].val = 0;
}
if (l == r) {
t[a[l] - 'a'][u].val = 1;
return ;
}
int M = (l + r) >> 1;
build(ls, l, M);
build(rs, M + 1, r);
for (int i = 0; i < 26; i++) pushup(u, i);
}
void modify(int u, int l, int r, int k, int c) {
if (l <= t[c][u].l && r >= t[c][u].r) {
maketag(u, k, c);
return ;
}
pushdown(u, c);
int M = (t[c][u].l + t[c][u].r) >> 1;
if (l <= M) modify(ls, l, r, k, c);
if (r > M) modify(rs, l, r, k, c);
pushup(u, c);
}
int query(int u, int l, int r, int c) {
if (l <= t[c][u].l && r >= t[c][u].r) return t[c][u].val;
pushdown(u, c);
int M = (t[c][u].l + t[c][u].r) >> 1, res = 0;
if (l <= M) res += query(ls, l, r, c);
if (r > M) res += query(rs, l, r, c);
return res;
}
void print(int u) {
if (t[0][u].l == t[0][u].r) {
for (int i = 0; i < 26; i++) {
if (t[i][u].val) {
printf("%c", i + 'a');
return ;
}
}
}
for (int i = 0; i < 26; i++) {
pushdown(u, i);
}
print(ls);
print(rs);
}
void solve(int l, int r, int i, int &p) {
int cnt = query(1, l, r, i);
if (!cnt) return ;
modify(1, l, r, 0, i);
modify(1, p, p + cnt - 1, 1, i);
p += cnt;
}
int main() {
//freopen("1.in", "r", stdin);
//freopen("1.out", "w", stdout);
scanf("%d%d", &n, &m);
scanf("%s", a + 1);
build(1, 1, n);
while (m--) {
int op, l, r;
scanf("%d%d%d", &l, &r, &op);
int p = l;
if (op == 1) {
for (int i = 0; i < 26; i++) {
solve(l, r, i, p);
}
} else {
for (int i = 25; i >= 0; i--) {
solve(l, r, i, p);
}
}
}
print(1);
return 0;
}
F. 1.树上操作
设节点
重要结论:
证明显然,略。
那么我们就将树上的问题转为区修区查问题,线段树维护即可。
代码似乎是复制的树链剖分模板然后改的,凑合着看吧。
Code
#include <bits/stdc++.h>
#define ls u << 1
#define rs u << 1 | 1
#define int long long
using namespace std;
const int N = 1e6 + 10;
typedef long long ll;
struct node{
ll val;
int l, r;
int lzy;
}t[N << 2];
struct edge{
int to, nxt;
}e[N * 2];
int n, m, r, p, vt, tot;
int a[N], fa[N], h[N], wc[N], sz[N];
int top[N], dfn[N], rdfn[N], dep[N];
//Graph
void add(int u, int v) {
e[tot].to = v;
e[tot].nxt = h[u];
h[u] = tot++;
}
//Quick Read
inline int rd() {
int s = 0, f = 1;
char ch = getchar();
while ('0' > ch || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while ('0' <= ch && ch <= '9') {
s = (s << 3) + (s << 1) + ch - 48;
ch = getchar();
}
return s * f;
}
//Segment Tree
inline void pushup(const int u) {
t[u].val = t[ls].val + t[rs].val;
}
inline void maketag(const int u, int x) {
t[u].lzy += x;
t[u].val += x * (t[u].r - t[u].l + 1);
}
inline void pushdown(const int u) {
maketag(ls, t[u].lzy);
maketag(rs, t[u].lzy);
t[u].lzy = 0;
}
void build(const int u, int l, int r) {
t[u].l = l, t[u].r = r, t[u].lzy = 0;
if (l == r) {
t[u].val = a[rdfn[l]];
return ;
}
int M = (l + r) >> 1;
build(ls, l, M);
build(rs, M + 1, r);
pushup(u);
}
void modify(const int u, int l, int r, int k) {
if (l > r) swap(l, r);
if (l <= t[u].l && r >= t[u].r) maketag(u, k);
else {
pushdown(u);
int M = (t[u].l + t[u].r) >> 1;
if (l <= M) modify(ls, l, r, k);
if (r > M) modify(rs, l, r, k);
pushup(u);
}
}
int query(const int u, int l, int r) {
if (l > r) swap(l, r);
if (l <= t[u].l && r >= t[u].r) return t[u].val;
pushdown(u);
int M = (t[u].l + t[u].r) >> 1, sum = 0;
if (l <= M) sum += query(ls, l, r);
if (r > M) sum += query(rs, l, r);
return sum;
}
//Deep First Search
void dfs1(int u, int fat) {
fa[u] = fat;
sz[u] = 1;
dep[u] = dep[fat] + 1;
for (int i = h[u]; i != -1; i = e[i].nxt) {
if (e[i].to == fat) continue;
int v = e[i].to;
dfs1(v, u);
sz[u] += sz[v];
if (sz[u] > sz[wc[u]]) wc[u] = v;
}
}
void dfs2(int u, int tp) {
dfn[u] = ++vt;
rdfn[vt] = u;
top[u] = tp;
if (wc[u]) dfs2(wc[u], tp);
for (int i = h[u]; i != -1; i = e[i].nxt) {
int v = e[i].to;
if (v == fa[u] || v == wc[u]) continue ;
dfs2(v, v);
}
}
//Tree Chain Partitioning
void modify_son(int x, int k) {
modify(1, dfn[x], dfn[x] + sz[x] - 1, k);
}
int query_son(int x) {
ll ans = 0;
ans = query(1, dfn[x], dfn[x] + sz[x] - 1);
return ans;
}
signed main() {
memset(h, -1, sizeof h);
tot = 0;
n = rd(), m = rd(), r = rd();
for (int i = 1; i <= n; ++i) a[i] = rd();
for (int i = 1; i < n; ++i) {
int u, v;
u = rd(), v = rd();
add(u, v);
add(v, u);
}
dfs1(r, 0);
dfs2(r, r);
build(1, 1, n);
while (m--) {
int op, x, y, u, k;
op = rd();
if (op == 1) {
x = rd(), k = rd();
modify_son(x, k);
} else {
x = rd();
printf("%lld\n", query_son(x));
}
}
return 0;
}
G. 2.取模问题
维护一个区间最大值和一个区间和。取模时如果发现区间最大值小于模数就直接返回,否则暴力递归取模。由于
Code
#include <iostream>
#include <cstdio>
#define ls u << 1
#define rs u << 1 | 1
#define int long long
using namespace std;
const int N = 1e5 + 10;
int n, q, a[N];
struct node{
int val;
int l, r;
int mx;
}t[N << 2];
inline int rd() {
int s = 0, f = 1;
char ch = getchar();
while ('0' > ch || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while ('0' <= ch && ch <= '9') {
s = (s << 3) + (s << 1) + ch - 48;
ch = getchar();
}
return s * f;
}
inline void pushup(const int u) {
t[u].val = t[ls].val + t[rs].val;
t[u].mx = max(t[ls].mx, t[rs].mx);
}
void build(const int u, int l, int r) {
t[u].l = l, t[u].r = r;
if (l == r) {
t[u].val = t[u].mx = a[l];
return ;
}
int M = (l + r) >> 1;
build(ls, l, M);
build(rs, M + 1, r);
pushup(u);
}
int query(const int u, int l, int r) {
if (l <= t[u].l && r >= t[u].r) return t[u].val;
int M = (t[u].l + t[u].r) >> 1, sum = 0;
if (l <= M) sum += query(ls, l, r);
if (r > M) sum += query(rs, l, r);
return sum;
}
void modify(const int u, int l, int r, int k) {
if (t[u].l == t[u].r) {
t[u].val = t[u].mx = k;
return;
}
int M = (t[u].l + t[u].r) >> 1;
if (l <= M) modify(ls, l, r, k);
if (r > M) modify(rs, l, r, k);
pushup(u);
}
void mmod(const int u, int l, int r, int k) {
if (t[u].mx < k) return;
if (t[u].l == t[u].r) {
t[u].val %= k;
t[u].mx %= k;
return;
}
int M = (t[u].l + t[u].r) >> 1;
if (l <= M) mmod(ls, l, r, k);
if (r > M) mmod(rs, l, r, k);
pushup(u);
}
signed main() {
cin >> n >> q;
for (int i = 1; i <= n; i++) a[i] = rd();
build(1, 1, n);
while (q--) {
int op = rd(), x = rd(), y = rd();
if (op == 1) {
printf("%lld\n", query(1, x, y));
} else if (op == 2) {
int k = rd();
mmod(1, x, y, k);
} else {
modify(1, x, x, y);
}
}
return 0;
}
H. 3.魔法传输
线段树维护差分数组,区间修改时将
Code
#include <bits/stdc++.h>
#define ls u << 1
#define rs u << 1 | 1
#define int long long
using namespace std;
const int N = 1e5 + 10;
int n, q, a[N];
struct node{
int val;
int l, r;
int lzy;
}t[N << 2];
void pushup(int u) {
t[u].val = t[ls].val + t[rs].val;
}
void maketag(int u, int x) {
t[u].lzy += x;
t[u].val += (t[u].r - t[u].l + 1) * x;
}
void pushdown(int u) {
if (t[u].lzy) {
maketag(ls, t[u].lzy);
maketag(rs, t[u].lzy);
t[u].lzy = 0;
}
}
void build(int u, int l, int r) {
t[u].val = t[u].lzy = 0;
t[u].l = l, t[u].r = r;
if (l == r) {
t[u].val = a[l];
return ;
}
int M = (l + r) >> 1;
build(ls, l, M);
build(rs, M + 1, r);
pushup(u);
}
void modify(int u, int l, int r, int k) {
if (l <= t[u].l && t[u].r <= r) maketag(u, k);
else {
pushdown(u);
int M = (t[u].l + t[u].r) >> 1;
if (l <= M) modify(ls, l, r, k);
if (r > M) modify(rs, l, r, k);
pushup(u);
}
}
int query(int u, int l, int r) {
if (l <= t[u].l && t[u].r <= r) return t[u].val;
pushdown(u);
int M = (t[u].l + t[u].r) >> 1, res = 0;
if (l <= M) res += query(ls, l, r);
if (r > M) res += query(rs, l, r);
return res;
}
signed main() {
scanf("%lld%lld", &n, &q);
build(1, 1, n + 1);
while (q--) {
char op[3];
scanf("%s", op);
if (op[0] == 'C') {
int l, r;
scanf("%lld%lld", &l, &r);
modify(1, l, r, 1);
modify(1, r + 1, r + 1, l - r - 1);
} else {
int x;
scanf("%lld", &x);
printf("%lld\n", query(1, 1, x));
}
}
return 0;
}
I. 4.和或异或
打一个标记,记录线段树这一层取或还是异或,然后合并(pushup)时将标记取反即可。
Code
#include <bits/stdc++.h>
#define int long long
#define ls u << 1
#define rs u << 1 | 1
using namespace std;
const int N = 1e6 + 10;
int n, q, a[N];
struct tree{
int l, r;
int fl, val;
}t[N << 2];
void pushup(int u) {
t[u].fl = t[ls].fl ^ 1;
if (t[u].fl == 1) t[u].val = t[ls].val | t[rs].val;
else t[u].val = t[ls].val ^ t[rs].val;
}
void build(int u, int l, int r) {
t[u].l = l, t[u].r = r;
if (l == r) {
t[u].val = a[l];
return ;
}
int M = (l + r) >> 1;
build(ls, l, M);
build(rs, M + 1, r);
pushup(u);
}
void modify(int u, int p, int k) {
if (t[u].l == t[u].r) {
t[u].val = k;
return ;
}
int M = (t[u].l + t[u].r) >> 1;
if (p <= M) modify(ls, p, k);
else modify(rs, p, k);
pushup(u);
}
signed main() {
scanf("%lld%lld", &n, &q);
for (int i = 1; i <= (1 << n); i++) {
scanf("%lld", &a[i]);
}
build(1, 1, (1 << n));
while (q--) {
int p, x;
scanf("%lld%lld", &p, &x);
modify(1, p, x);
printf("%lld\n", t[1].val);
}
return 0;
}
J. 5.括号匹配
记录一个区间内匹配括号数量、未匹配的左括号数量、未匹配的右括号数量。
合并(pushup)方式与 D 题类似,很简单。请读者对照代码自行理解。
Code
#include <bits/stdc++.h>
#define ls u << 1
#define rs u << 1 | 1
using namespace std;
const int N = 1e6 + 10;
int n, m;
char ch[N];
struct tree{
int l, r;
int la, ra, val;
}t[N << 2], k;
void pushup(int u) {
int tp = min(t[ls].la, t[rs].ra);
t[u].val = t[ls].val + t[rs].val + tp;
t[u].la = t[ls].la + t[rs].la - tp;
t[u].ra = t[ls].ra + t[rs].ra - tp;
}
void build(int u, int l, int r) {
t[u].l = l, t[u].r = r;
if (l == r) {
if (ch[l] == '(') t[u].la++;
if (ch[l] == ')') t[u].ra++;
return ;
}
int M = (l + r) >> 1;
build(ls, l, M);
build(rs, M + 1, r);
pushup(u);
}
void query(int u, int l, int r) {
if (l <= t[u].l && t[u].r <= r) {
int tp = min(k.la, t[u].ra);
k.val += (t[u].val + tp);
k.la += (t[u].la - tp);
k.ra += (t[u].ra - tp);
return ;
}
int M = (t[u].l + t[u].r) >> 1;
if (l <= M) query(ls, l, r);
if (r > M) query(rs, l, r);
pushup(u);
}
int main() {
cin >> n >> m;
cin >> ch + 1;
build(1, 1, n);
while (m--) {
int l, r;
scanf("%d%d", &l, &r);
k = {0, 0, 0, 0, 0};
query(1, l, r);
printf("%d\n", k.val * 2);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!