題解 [CSP-S 2021] 括號序列
阿新 • • 發佈:2021-10-26
送我退役的題,特此寫一篇題解
分析
讀題,發現給我一個現成的字串我都不會快速判是否合法,跳了(真實案例)
讀題,發現是求方案數、且較長區間的合法方案由較短區間的合法方案推來,所以是dp啊dp!
如果序列全是'?',顯然就是個裸的區間dp
如果有'('、'*'、')'呢?發現它們實質上比'?'少了兩個貢獻手段,所以對它們的處理可以自然地融入到dp中
於是我們設個狀態\(dp_{l, r}\)表示塞爆\([l,r]\)的合法方案數
規則1、3顯然很容易轉化成式子
但是對規則2,直接做可能會使一個方案在多個斷點被計算,所以加一維\(0/1\)表示方案最外層是否套括號
時間不夠用?預處理
細節在dp時繁多的限制
程式碼
#include <bits/stdc++.h> #define fast_cin ios::sync_with_stdio(false), cin.tie(nullptr) #define ll long long using namespace std; const int N = 505; const ll mod = 1e9 + 7; int n, K; char s[N]; bool allx[N][N]; ll dp[N][N][2]; ll qzh[N][N]; int reach[N]; inline bool isx(int x) { return s[x] == '*' || s[x] == '?'; } inline bool isl(int x) { return s[x] == '(' || s[x] == '?'; } inline bool isr(int x) { return s[x] == ')' || s[x] == '?'; } inline ll cl(ll x) { return x >= mod? x - mod : (x < 0? x + mod : x); } int main() { fast_cin; cin >> n >> K >> (s + 1); for(int l = 1; l <= n; ++l) { allx[l][l - 1] = 1; for(int r = l; r <= n; ++r) { if(isx(r)) allx[l][r] = 1; else break; } } for(int len = 2; len <= min(n, K + 2); ++len) { for(int l = 1; l <= n - len + 1; ++l) { int r = l + len - 1; if(isl(l) && isr(r) && allx[l + 1][r - 1]) { ++dp[l][r][0], ++dp[l][r][1]; } } } for(int r = 1; r <= n; ++r) { for(int i = r; i >= 1; --i) { qzh[i][r] = cl(qzh[i + 1][r] + dp[i][r][1]); } } for(int i = 1; i <= n; ++i) { for(int j = i + 1; j <= min(i + K + 1, n); ++j) { if(allx[i + 1][j - 1]) reach[i] = j; else break; } } for(int len = 4; len <= n; ++len) { for(int l = 1; l <= n - len + 1; ++l) { int r = l + len - 1; if(!isl(l) || !isr(r)) { qzh[l][r] = cl(qzh[l + 1][r] + dp[l][r][1]); // Death continue; } dp[l][r][0] = cl(dp[l][r][0] + dp[l + 1][r - 1][1]); dp[l][r][1] = cl(dp[l][r][1] + dp[l + 1][r - 1][1]); for(int k = 1; k <= min(K, r - l - 2); ++k) { if(allx[r - k][r - 1]) { dp[l][r][0] = cl(dp[l][r][0] + dp[l + 1][r - 1 - k][1]); dp[l][r][1] = cl(dp[l][r][1] + dp[l + 1][r - 1 - k][1]); } if(allx[l + 1][l + k]) { dp[l][r][0] = cl(dp[l][r][0] + dp[l + 1 + k][r - 1][1]); dp[l][r][1] = cl(dp[l][r][1] + dp[l + 1 + k][r - 1][1]); } } for(int i = l; i <= r - 1; ++i) { int j = min(reach[i], r); dp[l][r][1] = cl(dp[l][r][1] + dp[l][i][0] * cl(qzh[i + 1][r] - qzh[j + 1][r]) % mod); } qzh[l][r] = cl(qzh[l + 1][r] + dp[l][r][1]); } } cout << dp[1][n][1] << "\n"; return 0; }