CSP-S2021赛后总结
555我怎么这么菜,差点就保龄了。
T1:本来想着历年都有签到题,今年应该也差不多吧,结果我看了将近半小时一点想法都没有,最后只打了个O(n^2*logn),拿个40pts.
T2:想了一会,发现不可做,然后打暴力,然后15pts的暴力都没拿到,寄了。
T3:打了一个40pts的爆搜+剪枝,然后因为字符串输出寄了,如果n不是单调不降的话,寄了。(多测永远的狗
T4:题都没有看懂。听说(狗都不写的)网络流竟然能拿很多分?枯了。
update:补一个T2的订正(带注释)。区间计数,思维不难,但是细节非常多,值得反复回顾。
#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
char ch[505];
int num[505], f[505][505], sum[505][505], g[505][505];
bool checkstar(int l, int r) {return num[l - 1] == num[r];}
int main()
{
int n, m;
scanf("%d%d", &n, &m);
scanf("%s", ch + 1);
for (int i = 1; i <= n; i++) num[i] = num[i - 1] + (ch[i] == ')' || ch[i] == '(');
for (int len = 2; len <= n; len++)
{
for (int i = 1, j = len; i <= n && j <= n; i++, j++) //区间dp老套路:枚举区间长度,然后再枚举左端点
{
if (ch[i] == '*' || ch[i] == ')' || ch[j] == '(' || ch[j] == '*')
{
for (int k = 0; k < len && k <= m; k++)
{
if (checkstar(i, i + k - 1)) sum[i][j] = (sum[i][j] + f[i + k][j]) % mod;
else break;
}
continue;
} //必须满足i,j是一对括号
if (checkstar(i + 1, j - 1) && len - 2 <= m) f[i][j] = 1;//定义一判断它自身是否合法
f[i][j] = (f[i][j] + f[i + 1][j - 1]) % mod;//定义三第一类情况(A)
for (int k = 1; k <= len - 4 && k <= m; k++) //定义三第二种情况(SA)
{
if (checkstar(i + 1, i + k)) f[i][j] = (f[i][j] + f[i + k + 1][j - 1]) % mod;
else break;
}
for (int k = 1; k <= len - 4 && k <= m; k++) //定义三第三种情况(AS),写法类似于第二种情况
{
if (checkstar(j - k, j - 1)) f[i][j] = (f[i][j] + f[i + 1][j - k - 1]) % mod;
else break;
}
g[i][j] = f[i][j];//开一个g数组记录前后都是括号匹配的合法括号序列数量
for (int k = i + 1; k < j; k++) f[i][j] = (f[i][j] + 1ll * g[i][k - 1] * sum[k][j] % mod) % mod; //sum记录前面有一坨*(也可以没有),后面是合法括号序列的数量,那么定义二就可以直接转化为枚举中间的一个k,让前面是g,后面是sum,就满足了ASB(AB)的情况
for (int k = 0; k < len && k <= m; k++)
{
if (checkstar(i, i + k - 1)) sum[i][j] = (sum[i][j] + f[i + k][j]) % mod;
else break;
}
}
} printf("%d\n", f[1][n]);
}

浙公网安备 33010602011771号