【Codeforces Round #223 (Div. 1) C】Sereja and Brackets

题目链接

链接

翻译

给你一个区间,让你输出其中合法的括号序列(不要求连续)的最长的长度。

题解

线段树

在节点上维护当前这个区间内左右括号已经匹配了的对数 \(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;
}

posted @ 2020-12-23 17:49  AWCXV  阅读(101)  评论(0编辑  收藏  举报