Description
从前有个括号序列 s,满足 |s| = m。你需要统计括号序列对 (p, q) 的数量。
其中 (p, q) 满足 |p| + |s| + |q| = n,且 p + s + q 是一个合法的括号序列。
其中 (p, q) 满足 |p| + |s| + |q| = n,且 p + s + q 是一个合法的括号序列。
Input
从文件 bracket.in 中读入数据。第一行两个正整数 n, m。
第二行一个长度为 m 的括号序列,表示 s。
第二行一个长度为 m 的括号序列,表示 s。
Output
输出到文件 bracket.out 中。
输出一行一个整数,表示符合条件的 (p, q) 的数量对 10^9 + 7 取模的值。
输出一行一个整数,表示符合条件的 (p, q) 的数量对 10^9 + 7 取模的值。
Sample Input
【样例 1 输入】 4 1 ( 【样例 2 输入】 4 4 (()) 【样例 3 输入 4 3 (((
Sample Output
【样例 1 输出】 4 【样例 2 输出】 1 【样例 3 输出】 0
Data Constraint
对于 10% 的数据,n ≤ 20;
对于 25% 的数据,n ≤ 200;
对于另外 5% 的数据,n = m;
对于 55% 的数据,n − m ≤ 200;
对于 100% 的数据,1 ≤ m ≤ n ≤ 10^5, n − m ≤ 2000。
对于 25% 的数据,n ≤ 200;
对于另外 5% 的数据,n = m;
对于 55% 的数据,n − m ≤ 200;
对于 100% 的数据,1 ≤ m ≤ n ≤ 10^5, n − m ≤ 2000。
题解
- 我们可以把左括号看成+1,右括号看成-1
- 那么一个合法的括号序列就是左括号>=右括号
- 设f[i][j]为长度为i的序列 左括号比右括号多j个 的方案数
- 状态转移方程明显就是:f[i-1][j-1]+f[i-1][j+1]
- 那么考虑如何求最终的方案数
- 首先要满足左括号多于右括号,和 |p| + |s| + |q| = n
- 每次枚举一个i表示前面的p序列的长度,再枚举一个j表示p序列左括号比右括号多的个数
- 那么就是f[i][j]*f[n-m-i][j+tot](tot为原来序列中左括号比右括号多的个数)
代码
1 #include <cstdio> 2 #include <iostream> 3 #include <algorithm> 4 #include <cstring> 5 using namespace std; 6 const long long mo=1e9+7; 7 int n,m,tot,ltot; 8 char s[100010]; 9 long long ans,f[2010][2010]; 10 int main() 11 { 12 freopen("bracket.in","r",stdin); 13 freopen("bracket.out","w",stdout); 14 scanf("%d%d",&n,&m); 15 scanf("%s",s); 16 for (int i=0;i<m;i++) 17 { 18 if (s[i]=='(') tot++; else tot--; 19 if (i==0) ltot=tot; else ltot=min(tot,ltot); 20 } 21 f[0][0]=1; 22 for (int i=1;i<=n-m;i++) 23 for (int j=0;j<=i;j++) 24 if (j==0) f[i][j]=f[i-1][j+1]; 25 else f[i][j]=(f[i-1][j-1]+f[i-1][j+1])%mo; 26 for (int i=0;i<=n-m;i++) 27 for (int j=0;j<=i;j++) 28 if (j+tot<=n-m&&j+ltot>=0) 29 (ans+=f[i][j]*f[n-m-i][j+tot])%=mo; 30 printf("%lld",ans); 31 return 0; 32 }