题解 [CSP-S 2021] 括号序列
考场上由于策略原因打了个暴力就跑路了
DP只是简单想了想
- 貌似括号序列相关的方案数类的题常与区间DP有关?
考虑令 \(f_{l, r}\) 为区间 \([l, r]\) 为合法括号序列的方案数
\(u_{l, r}\) 为形如 \(AS\) 方案数
\(v_{l, r}\) 为形如 \(SA\) 方案数
\(t_{l, r}\) 为区间 \([l, r]\) 是合法 \(S\) 的方案数
转移按题意划分为两部分
但注意到会在 \(()()()\) 的情况算重
于是再定义一个 \(g_{l, r}\) 为区间 \([l, r]\) 为不能从中间划分为两个被括起来的括号序列的方案数即可
Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 510
#define ll long long
//#define int long long
int n, k;
char s[N];
const ll mod=1e9+7;
namespace force{
int pos[N], tot, ans;
char sta[N], sta2[N];
const char tab[]={'(', ')', '*'};
bool check() {
// cout<<"check: "; for (int i=1; i<=n; ++i) cout<<s[i]<<' '; cout<<endl;
int sum=0;
for (int i=1; i<=n; ++i)
if (s[i]=='(') ++sum;
else if (s[i]==')') --sum;
if (sum!=0) return 0;
int top=0;
for (int i=1,lst=0; i<=n; ++i) {
if (s[i]=='*') {
++lst;
if (lst>k) return 0;
}
else lst=0;
}
int lst=0;
for (int i=1; i<=n; ++i) {
if (s[i]=='*') lst=1;
else if (s[i]=='(') {
if (lst) sta[++top]='S', lst=0;
sta[++top]='(';
}
else {
if (lst) sta[++top]='S', lst=0;
sta[++top]=')';
}
}
if (lst) sta[++top]='S';
// cout<<"sta: "; for (int i=1; i<=top; ++i) cout<<sta[i]<<' '; cout<<endl;
int top2=0;
for (int i=1; i<=top; ++i) {
if (sta[i]=='(') sta2[++top2]='(';
else if (sta[i]==')') {
if (top2>0 && sta2[top2]=='(') sta2[top2]='A';
else if (top2>1 && sta2[top2-1]=='(' && sta2[top2]=='A') sta2[--top2]='A';
else if (top2>1 && sta2[top2-1]=='(' && sta2[top2]=='S') sta2[--top2]='A';
else if (top2>2 && sta2[top2-2]=='(' && sta2[top2-1]=='S' && sta2[top2]=='A') top2-=2, sta2[top2]='A';
else if (top2>2 && sta2[top2-2]=='(' && sta2[top2-1]=='A' && sta2[top2]=='S') top2-=2, sta2[top2]='A';
else sta2[++top2]=')';
}
else sta2[++top2]='S';
while (1) {
if (top2>1 && sta2[top2]=='A' && sta2[top2-1]=='A') sta2[--top2]='A';
else if (top2>2 && sta2[top2-2]=='A' && sta2[top2-1]=='S' && sta2[top2]=='A') top2-=2, sta2[top2]='A';
else break;
}
}
// cout<<"sta2: "; for (int i=1; i<=top2; ++i) cout<<sta2[i]<<' '; cout<<endl;
return bool(top2==1 && sta2[1]=='A');
}
void dfs(int u) {
if (u>tot) {if (check()) ++ans; return ;}
for (int i=1; i<=3; ++i) {
s[pos[u]]=tab[i-1];
dfs(u+1);
}
}
void solve() {
for (int i=1; i<=n; ++i) if (s[i]=='?') pos[++tot]=i;
dfs(1);
printf("%d\n", ans);
exit(0);
}
}
namespace task{
ll f[N][N], g[N][N], u[N][N], v[N][N], t[N][N];
void solve() {
for (int i=1; i<=n; ++i) t[i][i]=(s[i]=='*' || s[i]=='?');
for (int len=2; len<=n; ++len) {
for (int i=1,j; (j=i+len-1)<=n; ++i) {
for (int q=i; q<j; ++q) {
t[i][j]=(len<=k&&t[i][q]&&t[q+1][j]);
u[i][j]=(u[i][j]+t[i][q]*f[q+1][j])%mod;
v[i][j]=(v[i][j]+f[i][q]*t[q+1][j])%mod;
f[i][j]=(f[i][j]+g[i][q]*(u[q+1][j]+f[q+1][j]))%mod;
}
f[i][j]=(f[i][j]+((s[i]=='('||s[i]=='?')&&(s[j]==')'||s[j]=='?'))*(f[i+1][j-1]+t[i+1][j-1]+u[i+1][j-1]+v[i+1][j-1]+(len==2)))%mod;
g[i][j]=(g[i][j]+((s[i]=='('||s[i]=='?')&&(s[j]==')'||s[j]=='?'))*(f[i+1][j-1]+t[i+1][j-1]+u[i+1][j-1]+v[i+1][j-1]+(len==2)))%mod;
// printf("f[%d][%d]=%lld\n", i, j, f[i][j]);
}
}
printf("%lld\n", f[1][n]);
exit(0);
}
}
signed main()
{
//freopen("bracket.in", "r", stdin);
//freopen("bracket.out", "w", stdout);
scanf("%d%d", &n, &k);
scanf("%s", s+1);
// force::solve();
task::solve();
return 0;
}