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]);
}
posted @ 2021-10-24 18:47  Chasing-Dreams  阅读(57)  评论(0)    收藏  举报