CF 380C. Sereja and Brackets【线段树】
题目链接
思路
对于每一个线段树维护以下三个值:
a:结点内没有匹配的左括号数量
b:结点内没有匹配的右括号数量
c:结点内完成“()”匹配的数量
对于每一个结点u维护:
\(tr[u].c=tr[u*2].c+tr[u*2+1].c+min(tr[u*2].a,tr[u*2+1].b)\)
表示左子树中匹配完成的数量+右子树中匹配完成的数量+左子树中(的数量和右子树中)数量的匹配,取其最小值。
对于该节点剩余的a的数量和b的数量也只要对应的加一下并减去已经匹配完的数量即可。
\(tr[u].a = tr[u << 1].a + tr[u << 1 | 1].a - min(tr[u << 1].a, tr[u << 1 | 1].b);\)
\(tr[u].b = tr[u << 1].b + tr[u << 1 | 1].b - min(tr[u << 1].a, tr[u << 1 | 1].b);\)
在每一次的询问中也是如此维护即可。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int N = 1e6 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
char s[N];
struct Node {
int l, r;
int a, b, c;
}tr[N << 2];
struct node {
int a, b, c;
};
int n;
void push_up(int u) {
tr[u].c = tr[u << 1].c + tr[u << 1 | 1].c + min(tr[u << 1].a, tr[u << 1 | 1].b);
tr[u].a = tr[u << 1].a + tr[u << 1 | 1].a - min(tr[u << 1].a, tr[u << 1 | 1].b);
tr[u].b = tr[u << 1].b + tr[u << 1 | 1].b - min(tr[u << 1].a, tr[u << 1 | 1].b);
}
void build(int u, int l, int r) {
tr[u] = {l, r};
if(l == r) {
if(s[l] == '(') tr[u].a++;
else if(s[l] == ')') tr[u].b++;
return;
}
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
push_up(u);
}
node query(int u, int l, int r) {
if(l <= tr[u].l && tr[u].r <= r) {
return {tr[u].a, tr[u].b, tr[u].c};
}
int mid = tr[u].l + tr[u].r >> 1;
node x = {0, 0, 0}, y = {0, 0, 0}, cnt = {0, 0, 0};
if(l <= mid) x = query(u << 1, l, r);
if(r > mid) y = query(u << 1 | 1, l, r);
cnt.c = x.c + y.c;
int t = min(x.a, y.b);
cnt.c += t;
cnt.a = x.a + y.a - t;
cnt.b = x.b + y.b - t;
return cnt;
}
void solve() {
scanf("%s", s + 1);
n = strlen(s + 1);
build(1, 1, n);
int m;
scanf("%d", &m);
while(m--) {
int l, r;
scanf("%d%d", &l, &r);
printf("%d\n", query(1, l, r).c * 2);
}
}
int main() {
// freopen("in.txt", "r", stdin);
solve();
return 0;
}