/*
區間線段樹 區間覆蓋 括弧序列 lazy
題意:給一串括弧序列,問[l,r]的括弧序列是否合法。合法的定義:1,Empty 2,S合法,則(S)合法 3,A和B合法,則AB合法。有兩種操作,一種一般區間修改(reverse, set),還有Query.
思路:之前一遇到括弧序列就跪,因為我到現在都搞不清楚what is regular brackets sequence...
所以在大牛blog上看到下面這句話的時候心情很激動~~from : http://hi.baidu.com/zyz913614263/item/ed68843a6839ecd2392ffa2b
*** "把左括弧當做-1,右括弧當做1,對於一個區間,只要從左至右的和的最大值為0,並且區間的和為0,則這個區間就是完全符合的" ***
知道上面這個定理後,接下來都是自己寫的。這道題細節還是挺多的。
(1)在Update和Query下子節點時,都要先put_down。
(2)我的lazy標記只有一個,後來看別人的代碼時發現他們用了兩個,一個is_reverse, 一個is_set。其實可以合成一個的。
(3)細節多WA在有lazy來時和put_down的時候的處理:儲存lazy時要更新節點屬性put_self。剛開始時我把更新lazy值和更新節點屬性分開了,後來發現不對,前後的lazy值會影響節點屬性的更新,所以乾脆都扔到change()裡,方便多了。
*** "把左括弧當做-1,右括弧當做1,對於一個區間,只要從左至右的和的最大值為0,並且區間的和為0,則這個區間就是完全符合的" ***
*/
//Bracket Sequence UESTC 1546#include <stdio.h>#include <string.h>#include <algorithm>using namespace std;#define debug printf("!\n")#define MAXN 100005#define MID(x, y) (((x)+(y)) >> 1)#define L(x) ((x)<<1)#define R(x) (((x)<<1)|1)typedef pair<int,int> Pair;struct Node { int l,r,min,max,end; char lazy;} f[MAXN << 2];char str[100005];int n;void Update(int , int , int , char);void put_self(int u, char op) //更新節點u{ if(op == '(' || op == ')') { int tmp = (op == '(' ? -1 : 1); f[u].end = tmp * (f[u].r - f[u].l + 1); f[u].min = min(tmp, f[u].end); f[u].max = max(tmp, f[u].end); } else { f[u].end = - f[u].end; int tmp = f[u].max; f[u].max = -f[u].min; f[u].min = -tmp; }}void change(int u, char arrive) //遇到修改[l,r]操作時邊lazy,邊更新自己的屬性(max,min,end){ if(arrive == '(' || arrive == ')') { put_self(u, arrive); f[u].lazy = arrive; } else if(arrive == 'r') { put_self(u, 'r'); if(f[u].lazy == 'r') f[u].lazy = '\0'; else if(f[u].lazy == '\0') f[u].lazy = 'r'; else f[u].lazy = (f[u].lazy == ')' ? '(' : ')'); }}void put_up(int u){ Node & ll = f[L(u)]; Node & rr = f[R(u)]; f[u].end = ll.end + rr.end; f[u].max = max(ll.max, ll.end+rr.max); f[u].min = min(ll.min, ll.end+rr.min);}void put_down(int u){ int mid = MID(f[u].l, f[u].r); Update(L(u), f[u].l, mid, f[u].lazy); Update(R(u), mid+1, f[u].r, f[u].lazy); f[u].lazy = '\0';}void build(int u, int l, int r){ f[u].l = l, f[u].r = r, f[u].lazy = '\0'; if(l == r) { f[u].min = f[u].max = f[u].end = (str[l] == '(' ? -1 : 1); //小錯誤大煩惱,忘了初始化min...debug了好久 return ; } int mid = MID(l, r); build(L(u), l, mid); build(R(u), mid+1, r); put_up(u);}Pair Query(int u, int l, int r){ if(l == f[u].l && f[u].r == r) { return Pair (f[u].end, f[u].max); } if(f[u].lazy != '\0') put_down(u); //所以說,小錯誤有大關係...漏了判斷就得debug很久 int mid = MID(f[u].l, f[u].r); if(r <= mid) return Query(L(u), l, r); else if(mid < l) return Query(R(u), l, r); else { Pair p1 = Query(L(u), l, mid) , p2 = Query(R(u), mid+1, r); return Pair(p1.first + p2.first, max(p1.second, p1.first + p2.second)); }}void Update(int u, int l, int r, char op){ if(l == f[u].l && f[u].r == r) { change(u, op); //把lazy的更新,max/end的更新放到change裡了 return ; } if(f[u].lazy != '\0')put_down(u); int mid = MID(f[u].l, f[u].r); if(r <= mid) Update(L(u), l, r, op); else if(mid < l) Update(R(u), l, r, op); else Update(L(u), l, mid, op), Update(R(u), mid+1, r, op); put_up(u);}int main(){ int cases, Cas = 0, query, l, r; char op[10]; scanf("%d", &cases); while(cases--) { printf("Case %d:\n", ++Cas); scanf("%d%s%d", &n, str, &query); build(1, 0, n-1); while(query--) { scanf("%s%d%d\n", op, &l, &r); if(op[0] == 'q') { Pair p = Query(1, l,r); puts(p.first == 0 && p.second < 1 ? "YES" : "NO");// printf("!! %d %d\n", p.first, p.second); } else if(op[0] == 's') { scanf("%s", str); Update(1, l, r, str[0]); } else Update(1, l, r, 'r'); } puts(""); } return 0;}