Flipping Parentheses(CSU1542 线段树)
http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1542
赛后发现这套题是2014东京区域赛的题目,看了排名才发现自己有多low = =!
题目大意是这样的,给一个已经匹配了的括号序列,现每次操作就是在位置p将这个位置的括号反转,问要达到将这个序列重新变成匹配序列,最左侧能够操作第几个括号达到目的。
这里,我的思路是用一个数组a,a[i]表示的是前i个括号,左括号'('与右括号')'的数量之差,题目数据就可以表示为:
s: ( ( ( ) ) )
a: 1 2 3 2 1 0
这样,一个匹配的括号序列对应的数组一定满足两个条件:1、数组没有一个元素为负 2、数组是以0结尾
现操作时,若是将'(' 转化为')' ,比如将位置4的括号反转,对应数组:
s: ( ( ( ( ) )
a: 1 2 3 4 3 2
可以看做是将数组位置4以后的每个元素都+2(反之,若是')'转化为'(',那么就是那个位置以后每个元素都-2)
可以看到:
对应第一种操作,将'(' 转化为')', 要达到匹配序列,只需要将括号序列里的第一个右括号')'翻转即可(因为此时为+2操作,又只可以操作右括号,所以操作第一个必然是最优的)
对应第二种操作,将')'转化为'(',则是找到从末尾往前找到最早的一个a[p]>=2的位置p,且a[p ~ n] >= 2全部成立,(因为是-2操作,所以必须保证在-2前这个数是>=2的)
计算时,可以使用线段树(因为有区间操作),节点保存的内容包括左右括号数量之差a,延时标记s,以及f = a[i] - i的值(若a[i] - i < 0,说明在区间[1, i]一定存在一个左括号,供查找第一个左括号时使用),而若要找到从后往前最长连续的a[p ~ n] >= 2的位置p,只需要判断区间的最小值是否>=2,查找方式与上一个类似。
查询时区间保存的是这个区间的 a 值和 f 值的最小值。区间修改+极值查询。
1 #include <map> 2 #include <set> 3 #include <stack> 4 #include <queue> 5 #include <cmath> 6 #include <ctime> 7 #include <vector> 8 #include <cstdio> 9 #include <cctype> 10 #include <cstring> 11 #include <cstdlib> 12 #include <iostream> 13 #include <algorithm> 14 using namespace std; 15 #define INF 0x3f3f3f3f 16 #define inf (-((LL)1<<40)) 17 #define lson k<<1, L, mid 18 #define rson k<<1|1, mid+1, R 19 #define mem0(a) memset(a,0,sizeof(a)) 20 #define mem1(a) memset(a,-1,sizeof(a)) 21 #define mem(a, b) memset(a, b, sizeof(a)) 22 #define FIN freopen("in.txt", "r", stdin) 23 #define FOUT freopen("out.txt", "w", stdout) 24 #define rep(i, a, b) for(int i = a; i <= b; i ++) 25 26 template<class T> T CMP_MIN(T a, T b) { return a < b; } 27 template<class T> T CMP_MAX(T a, T b) { return a > b; } 28 template<class T> T MAX(T a, T b) { return a > b ? a : b; } 29 template<class T> T MIN(T a, T b) { return a < b ? a : b; } 30 template<class T> T GCD(T a, T b) { return b ? GCD(b, a%b) : a; } 31 template<class T> T LCM(T a, T b) { return a / GCD(a,b) * b; } 32 33 //typedef __int64 LL; 34 typedef long long LL; 35 const int MAXN = 1010; 36 const int MAXM = 20010; 37 const double eps = 1e-4; 38 39 int n, q, p, len; 40 char s[310000]={"1"}; 41 struct Node { 42 int f, a, s; 43 }t[310000<<2]; 44 45 char rev(char c) { return (c == '(') ? ')' : '('; } 46 47 void buildTree(int k, int L, int R, int p, int a) 48 { 49 t[k].s = 0; 50 if(L == R) { 51 t[k].f = a - L; t[k].a = a; 52 return ; 53 } 54 int mid = (L + R) >> 1; 55 if(p > mid) buildTree(rson, p, a); 56 else buildTree(lson, p, a); 57 t[k].f = min(t[k<<1].f, t[k<<1|1].f); 58 t[k].a = min(t[k<<1].a, t[k<<1|1].a); 59 } 60 61 void init() 62 { 63 mem0(t); 64 int sum = 0; 65 len = strlen(s) - 1; 66 rep (i, 1, len) { 67 sum += (s[i] == '(') ? 1 : -1; 68 buildTree(1, 1, len, i, sum);//建树 69 } 70 } 71 72 //向下传延时标记 73 void pushDown(int k) 74 { 75 t[k<<1].s += t[k].s; t[k<<1].a += t[k].s; t[k<<1].f += t[k].s; 76 t[k<<1|1].s += t[k].s; t[k<<1|1].a += t[k].s; t[k<<1|1].f += t[k].s; 77 t[k].s = 0; 78 } 79 80 //更新操作,将区间[p, len]的所有值都+val 81 void update(int k, int L, int R, int p, int val) 82 { 83 if(p <= L) { 84 t[k].s += val; 85 t[k].a += val; 86 t[k].f += val; 87 return ; 88 } 89 pushDown(k); 90 int mid = (L + R) >> 1; 91 if(p <= mid) update(lson, p, val);//左侧可能需要更新 92 update(rson, p, val);//右侧是一定要更新的,因为需要更新的区间为[p, len] 93 t[k].f = min(t[k<<1].f, t[k<<1|1].f); 94 t[k].a = min(t[k<<1].a, t[k<<1|1].a); 95 } 96 97 //查询最左侧的右括号 98 int query1(int k, int L, int R) 99 { 100 if(L == R) return L; 101 int mid = (L + R) >> 1; 102 pushDown(k); 103 if(t[k<<1].f < 0) return query1(lson); 104 return query1(rson); 105 } 106 107 //查询从len往前连续的最长的满足a>=2的点,也就是最后一个<2的位置+1 108 int query2(int k, int L, int R) 109 { 110 if(L == R) return min(len, L + 1); 111 int mid = (L + R) >> 1; 112 pushDown(k); 113 if(t[k<<1|1].a < 2) return query2(rson); 114 return query2(lson); 115 } 116 117 int main() 118 { 119 //FIN; 120 while(~scanf("%d %d%*c %s", &n, &q, s + 1)) { 121 init(); 122 rep (i, 0, q - 1) { 123 scanf("%d", &p); 124 s[p] = rev(s[p]); update(1, 1, len, p, s[p] == ')' ? -2 : 2); 125 if(s[p] == ')') p = query1(1, 1, len);//find first ) 126 else p = query2(1, 1, len); //find last < 2 127 s[p] = rev(s[p]); update(1, 1, len, p, s[p] == ')' ? -2 : 2); 128 printf("%d\n", p); 129 } 130 } 131 return 0; 132 }