卧薪尝胆,厚积薄发。
提高A组模拟 简单的序列
Date: Sun Aug 12 16:08:23 CST 2018
In Category:
NoCategory
Description:
有个括号序列
$s$
,满足
$|s|=m$
。统计括号序列对
$(p,q)$
的数量。其中
$(p,q)$
满足
$|p| + |s| + |q| = n$
,且
$ p + s + q $
是一个合法的括号序列。
$1\le n\le 2000$
Soltuion:
我的做法:
$f[i][j]$
表示
$i$
个括号,两两匹配后还能剩
$j$
个括号,每次枚举最前面的合法的括号序列长度并用卡塔兰数算方案数转移
$O(n^3)$
,不可前缀和优化,不可数据结构优化。得分
$75$
。
正解:
$f[i][j]$
表示
$i$
个左括号
$j$
个右括号的方案数
$(i\ge j)$
,转移时枚举最后一个左括号右边的右括号个数
$k$
,有
$f[i][j]=\sum_{k=0}^nf[i-1][k]$
。
发现
$k$
与状态无关,可以前缀和优化。
最后统计答案时枚举左边括号个数和左边左括号个数累加答案即可。
Code:
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<queue>
#include<cstring>
using namespace std;
int n,m;
#define MAXL 2010
#define MAXN 100010
typedef unsigned long long ll;
#define MOD 1000000007
ll f[MAXL][MAXL];
ll s[MAXL][MAXL];
inline char getc()
{
register char c = getchar();
while(c != '(' && c != ')')c = getchar();
return c;
}
char bra[MAXN];
int main()
{
freopen("bracket.in","r",stdin);
freopen("bracket.out","w",stdout);
scanf("%d%d",&n,&m);
if(n % 2 == 1)
{
puts("0");
return 0;
}
deque<char> q;
for(int i = 1;i <= m;++i)
{
if(getc() == '(')q.push_back('(');
else
{
if(!q.empty() && q.back() == '(')q.pop_back();
else q.push_back(')');
}
}
int len = 0;
int lef = 0,rig = 0;
for(;;)
{
if(q.empty())break;
if(q.front() == ')')++lef;
else ++rig;
bra[++len] = q.front();q.pop_front();
}
f[0][0] = 1;
for(int i = 0;i <= n - m;++i)s[0][i] = 1;
for(int i = 1;i <= n - m;++i)
{
f[i][0] = 1;
s[i][0] = f[i][0];
for(int j = 1;j <= i;++j)
{
f[i][j] = s[i - 1][j];
}
for(int j = 1;j <= n - m;++j)
{
s[i][j] = (s[i][j - 1] + f[i][j]) % MOD;
}
}
ll ans = 0;
for(int i = lef;i <= n - m;++i)
{
for(int j = (i + lef + 1) / 2;j <= i;++j)
{
ans = (ans + f[j][i - j] * f[(2 * j - 2 * i - lef + rig + n - m) / 2][(-2 * j + lef - rig + n - m) / 2] % MOD) % MOD;
}
}
cout << ans << endl;
fclose(stdin);
fclose(stdout);
return 0;
}
In tag:
DP-前缀和优化DP
Copyright © 2020
wjh15101051
ღゝ◡╹)ノ♡