题目翻译
题目给我们一个数字n,代表一个有着n个好对的括号序列,一个好对(i, j) (i < j) 满足s[i] == ‘(‘, s[j] == ‘)’同时去掉i,j两个字符,区间内剩余的子串是一个合法的平衡括号字符串,可以证明对于一个长度位2 * n的括号序列一定有n个好对。题目依次给出n个好对,我们需要计算出前i个好对确定的情况下有多少种符号要求的合法括号字符串(i从0到n)。
思路
首先我们根据题目的定义我们发现对于一个好对[l, r]其中l - 1, r - 1可以填任何合法的括号序列,所以我们需要预处理出长度x时的合法括号序列数,这可以通过dp[len] [diff]来n ^ 2的dp处理出来,状态表示长度为len时平衡度为diff的括号序列数量。然后考虑如何根据确定了的好对来计算可能的括号序列种类。我们发现一个好对就可以把其包含区间的种类给算出来,并且按照定义好对不会相交,只有可能包含,包含时我们可以用来填的空要是还没有确定的,也就是去掉包含的好对长度,于是对于前i个,我们可以通过维护左端点第一关键,右端点第二关键,有序的好对前缀,来O(n)的计算出每个好对还可以填写的空,结合dp[len] [0]相乘计数即可,最后乘上剩余所有没填的空的贡献即可。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
| constexpr ll mod = 998244353; int N = 5000;
std::vector<std::vector<int>> dp(2 * N + 1, std::vector<int>(2 * N + 1, 0));
void init(){ dp[0][0] = 1; for(int i = 1; i <= 2 * N; i++){ for(int j = 0; j <= i; j++){ if(j > 0) dp[i][j] = (1LL * dp[i][j] + dp[i - 1][j - 1]) % mod; if(j < N) dp[i][j] = (1LL * dp[i - 1][j + 1] + dp[i][j]) % mod; } } }
void solve(){ int n; std::cin >> n; std::vector<std::pair<int, int>> cur(n + 1); for(int i = 1; i <= n; i++) std::cin >> cur[i].ff >> cur[i].ss;
std::vector<ll> res; res.push_back(dp[2 * n][0]);
std::vector<int> occur(n + 1, 0); cur[0].ff = 0; cur[0].ss = 2 * n + 1;
std::vector<int> qu;
for(int i = 1; i <= n; i++){ int ind = -1; for(int j = 0; j < i - 1; j++){ if(cur[qu[j]].ff <= cur[i].ff || (cur[qu[j]].ff == cur[i].ff && cur[qu[j]].ss <= cur[i].ss)){ ind = j; } else break; }
std::vector<int> now; for(int j = 0; j <= ind; j++) now.push_back(qu[j]); now.push_back(i); for(int j = ind + 1; j < qu.size(); j++) now.push_back(qu[j]); qu = now; std::stack<int> stk; for(int j = 0; j <= i; j++) occur[j] = 0;
stk.push(0); for(auto ind : qu){ int l = cur[ind].ff; int r = cur[ind].ss; while(cur[stk.top()].ss < r) stk.pop(); occur[stk.top()] += r - l + 1; stk.push(ind); } ll ans = 1; for(int j = 1; j <= i; j++){ ll use = 2 + occur[j]; ll last = (cur[j].ss - cur[j].ff + 1) - use; ans = (ans * dp[last][0]) % mod; } ll last = 2 * n - occur[0]; ans = (ans * dp[last][0]) % mod;
res.push_back(ans); } for(int i = 0; i < res.size(); i++) std::cout << res[i] << sendl[i == res.size() - 1]; }
|