题解 [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;
}
上一篇:Auto.JS实现抖音,刷宝等刷视频app,自动点赞,自动滑屏,自动切换视频


下一篇:日期类问题