SYZOJ 20220218 E 合法括号串 & CF380C Sereja and Brackets

written on 2022-02-22

这题很有意思,因为我以前从来没有用这种写法写过题

题目要求的是区间内可匹配的括号数。

直接思路是用栈在区间内模拟,加上切分操作,亲测可拿 \(70pts\)

正解是用 线段树 维护,维护什么呢?

注意到题目要求的是区间内可匹配的括号数,直接求肯定有难度,因此在看了题解后我发现还要维护两个值, 该区间内未匹配的左括号数以及右括号数

重点来看线段树的 \(pushup\) (第二次颠覆了我对线段树只能进行区间求和以及区间查询的认知(第一次是 \(lost cows\) ))。

先上代码:

void push_up(int p)
{
	int k=min(vall(p<<1),valr(p<<1|1));
	now(p)=now(p<<1)+now(p<<1|1)+k;//now(p)   区间内可匹配的括号组数
	vall(p)=vall(p<<1)+vall(p<<1|1)-k;//vall(p)   区间内不能匹配的左括号数
	valr(p)=valr(p<<1)+valr(p<<1|1)-k;//valr(p)   区间内不能匹配的右括号数
}

对于 \(now\) ,显然是左右两个子节点 可以匹配的 \(now\) 之和 再加上 左边未匹配的左括号 与 右边未匹配右括号 的最小值(即为 \(k\)

对于 \(vall\) ,它的值是由 左子节点和右子节点的 \(vall\) 之和减去新匹配的括号数(即为\(k\)

对于 \(valr\) ,它的值是由 左子节点和右子节点的 \(valr\) 之和减去新匹配的括号数(即为\(k\)

最后要注意在递归询问时也动态更新(但不需赋值)。

code

#include<bits/stdc++.h>
#define N 1000005
using namespace std;
int n;
char a[N];
struct f
{
	int l,r,now,vall,valr;
	#define l(p) t[p].l
	#define r(p) t[p].r
	#define vall(p) t[p].vall
	#define valr(p) t[p].valr
	#define now(p) t[p].now
}t[N<<2];
void push_up(int p)
{
	int k=min(vall(p<<1),valr(p<<1|1));
	now(p)=now(p<<1)+now(p<<1|1)+k;
	vall(p)=vall(p<<1)+vall(p<<1|1)-k;
	valr(p)=valr(p<<1)+valr(p<<1|1)-k;
}
void build(int p,int l,int r)
{
	l(p)=l,r(p)=r;
	if(l==r)
	{
		if(a[l]=='(') vall(p)=1;
		else if(a[l]==')') valr(p)=1;
		return ;
	}
	int mid=l+r>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	push_up(p);
}
f ask(int p,int l,int r)
{
	if(l<=l(p)&&r>=r(p)) return t[p];
	f a=(f){0,0,0,0,0},b=(f){0,0,0,0,0};
	if(l<=r(p<<1)) a=ask(p<<1,l,r);
	if(r>=l(p<<1|1)) b=ask(p<<1|1,l,r);
	int k=min(a.vall,b.valr);
	return (f){0,0,a.now+b.now+k,a.vall+b.vall-k,a.valr+b.valr-k};
}
int main()
{
	scanf("%s",a+1);
	n=strlen(a+1);
	build(1,1,n);
	int q;
	scanf("%d",&q);
	while(q--)
	{
		int l,r;
		scanf("%d%d",&l,&r);
		printf("%d\n",ask(1,l,r).now*2);
	}
}
posted @ 2022-07-31 17:37  Freshair_qprt  阅读(19)  评论(0编辑  收藏  举报