1. 程式人生 > 實用技巧 >【Codeforces Round #223 (Div. 1) C】Sereja and Brackets

【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;
}