1. 程式人生 > 其它 >題解 [CSP-S 2021] 括號序列

題解 [CSP-S 2021] 括號序列

送我退役的題,特此寫一篇題解

分析

讀題,發現給我一個現成的字串我都不會快速判是否合法,跳了(真實案例)

讀題,發現是求方案數、且較長區間的合法方案由較短區間的合法方案推來,所以是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;
}