【HDU5357】Easy Sequence 题解(栈的应用)
新博客:debug18.com 使用hexo搭建,欢迎来踩~
传送门:http://acm.hdu.edu.cn/showproblem.php?pid=5357
题目大意:给你一个括号序列(不保证合法),求对于每个字符,包含这个字符的合法括号序列的总方案数。
容易发现,对于一个匹配的括号“(...)”,其里面的内容对其总方案数并无影响,必须全部包括,所以只用考虑外面的。
其覆盖方案分为两种类型:
例子:"...()(...()()()...)()..." (根据合法序列是否覆盖了“包括了它的最小的括号对”进行分类)
- 第一部分:倘若它的合法序列覆盖了整个括号对,那么这一部分的方案数等于括号对的总方案数
- 第二部分:倘若它的合法序列在括号对内,那么这种合法序列一定覆盖了其两边有与其“同级别"(即互不包含)的括号对,设左边有a个,右边有b个,则这一部分的方案数为 (a + 1) * (b + 1)
合并在一起就求出了每个位置的方案数
做法:计算出第 i 个括号的匹配括号位置 match[i],包含它的括号对的左括号位置 pre[i],以及:
- 对于左括号,其右边的“同级别”的括号个数(包括自己)forward[i] = forward[match[i]+1] + 1
- 对于右括号,其左边的“同级别”的括号个数(包括自己)backward[i] = backward[match[i]-1] + 1
其中,match[i] 和 pre[i] 可以对序列用栈扫一遍求出,具体做法是:遇到左括号压进栈,遇到右括号就弹栈,之前的栈顶元素与当前元素相互 match,现在的栈顶元素成为当前元素及其 match 的 pre。
最后,ans[i] = ans[match[i]] = ans[pre[i]] + forward[i] * backward[match[i]]
我模板里的输出优化写丑了,导致一直WA……
代码:
1 #include <cstdio> 2 #include <cstdlib> 3 #include <cstring> 4 #include <cmath> 5 #include <cctype> 6 #include <cassert> 7 #include <ctime> 8 #include <algorithm> 9 #include <utility> 10 #include <functional> 11 12 #define X first 13 #define Y second 14 #define MP make_pair 15 #define BP push_back 16 #define SZ(x) ((int)(x).size()) 17 #define ALL(x) (x).begin(), (x).end() 18 #define DEBUG(...) fprintf(stderr, __VA_ARGS__) 19 20 using namespace std; 21 22 typedef long long LL; 23 typedef long double LD; 24 typedef pair<int, int> Pii; 25 26 const int oo = 0x3f3f3f3f; 27 28 template<class T> T read(T &x) 29 { 30 int f = 1; 31 char ch = getchar(); 32 while (!isdigit(ch)) { 33 if (ch == '-') 34 f = -1; 35 ch = getchar(); 36 } 37 for (x = 0; isdigit(ch); ch = getchar()) 38 x = 10 * x + ch - '0'; 39 40 return x *= f; 41 } 42 template<class T> void write(T x) 43 { 44 if (x < 0) { 45 putchar('-'); 46 x = -x; 47 } 48 static char s[20]; 49 int top; 50 for (top = 0; x; x /= 10) 51 s[++top] = x % 10 + '0'; 52 if (top == 0) 53 putchar('0'); 54 else while (top) 55 putchar(s[top--]); 56 } 57 // End of template 58 59 const int MAXN = 1e6 + 5, MOD = 1e9 + 7; 60 int N; 61 char S[MAXN]; 62 63 int match[MAXN], pre[MAXN]; 64 void getMatch() 65 { 66 static int st[MAXN]; 67 int top = 0; 68 memset(match, 0, sizeof(int) * (N + 1)); 69 memset(pre, 0, sizeof(int) * (N + 1)); 70 for (int i = 1; i <= N; i++) { 71 if (S[i] == 0) 72 st[++top] = i; 73 else if (top) { 74 match[st[top]] = i; 75 match[i] = st[top]; 76 top--; 77 if (top) 78 pre[match[i]] = st[top]; 79 } 80 } 81 } 82 83 LL solve() 84 { 85 static int forward[MAXN], backward[MAXN], ans[MAXN]; 86 87 memset(forward, 0, sizeof(int) * (N + 2)); 88 for (int i = N; i; i--) { 89 if (S[i] == 0 && match[i]) 90 forward[i] = forward[match[i]+1] + 1; 91 } 92 memset(backward, 0, sizeof(int) * (N + 2)); 93 for (int i = 1; i <= N; i++) { 94 if (S[i] == 1 && match[i]) 95 backward[i] = backward[match[i]-1] + 1; 96 } 97 98 LL res = 0; 99 memset(ans, 0, sizeof(int) * (N + 2)); 100 for (int i = 1; i <= N; i++) { 101 if (S[i] == 0 && match[i]) 102 ans[i] = ans[match[i]] = (ans[pre[i]] + (LL)forward[i] * backward[match[i]] % MOD) % MOD; 103 res += (LL)i * ans[i] % MOD; 104 } 105 return res; 106 } 107 108 int main() 109 { 110 #ifndef ONLINE_JUDGE 111 freopen("tmp.in", "r", stdin); 112 freopen("tmp.out", "w", stdout); 113 #endif 114 int T; 115 read(T); 116 while (T--) { 117 scanf("%s", S + 1); 118 N = strlen(S + 1); 119 for (int i = 1; i <= N; i++) 120 S[i] = S[i] == ')'; 121 getMatch(); 122 write(solve()); putchar('\n'); 123 } 124 125 return 0; 126 }