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;
}
posted @ 2021-04-29 14:39  这知识他不进我的脑子  阅读(51)  评论(0)    收藏  举报