题目链接
翻译
给你一个区间,让你输出其中合法的括号序列(不要求连续)的最长的长度。
题解
线段树
在节点上维护当前这个区间内左右括号已经匹配了的对数 (mb[rt])
另外维护两个用于合并的数组 (lb[rt]) 表示还没有用来匹配的左括号的数目,(rb[rt]) 则是右括号。
(trick) 就是,在合并的时候因为区间已经足够小了。两个子区间不考虑有匹配的情况了,现在只要考虑
左括号在左区间然后右括号在右区间的情况即可。
询问的时候也要做类似的合并操作,并且要记录两个询问的子区间对应的 (lb[rt]) 和 (rb[rt])(得重新算)。
代码
#include <bits/stdc++.h>
#define lson l,mid,rt*2
#define rson mid+1,r,rt*2+1
#define LL long long
using namespace std;
const int N = 1e6;
char s[N+10];
int m,lb[N*4+10],rb[N*4+10],mb[N*4+10];
void build(int l,int r,int rt){
if (l == r){
if (s[l] == '('){
lb[rt] = 1;
}else{
rb[rt] = 1;
}
return;
}
int mid = (l+r)/2;
build(lson);build(rson);
mb[rt] = mb[rt*2] + mb[rt*2+1];
int t = min(lb[rt*2],rb[rt*2+1]);
mb[rt] = mb[rt] + t;
lb[rt] = lb[rt*2] + lb[rt*2+1] - t;
rb[rt] = rb[rt*2] + rb[rt*2+1] - t;
}
int _query(int L,int R,int l,int r,int rt,int &lbRest,int &rbRest){
if (L<=l && r <= R){
lbRest = lb[rt];rbRest = rb[rt];
return mb[rt];
}
int mid = (l+r)/2;
if (mid<L){
return _query(L,R,rson,lbRest,rbRest);
}else if (R<=mid){
return _query(L,R,lson,lbRest,rbRest);
}else{
int tlbRest,trbRest;
int t1 = _query(L,mid,lson,lbRest,rbRest);
int t2 = _query(mid+1,R,rson,tlbRest,trbRest);
int t = min(lbRest,trbRest);
lbRest = lbRest + tlbRest - t;
rbRest = rbRest + trbRest - t;
return t1 + t2 + t;
}
}
int main(){
// freopen("C://1.cppSourceProgram//rush.txt","r",stdin);
ios::sync_with_stdio(0),cin.tie(0);
cin >> (s+1);
int n = strlen(s+1);
build(1,n,1);
cin >> m;
while (m--){
int l, r;
cin >> l >> r;
int xx1,xx2;
cout << _query(l,r,1,n,1,xx1,xx2)*2 << endl;
}
return 0;
}