【Codeforces Round #223 (Div. 1) C】Sereja and Brackets
阿新 • • 發佈:2020-12-23
題目連結
翻譯
給你一個區間,讓你輸出其中合法的括號序列(不要求連續)的最長的長度。
題解
線段樹
在節點上維護當前這個區間內左右括號已經匹配了的對數 \(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; }