自我感觉是一个签到题,但是想了很久,最后求助才能苟活。
按照它的定义,很容易想到如何求出合法的序列:
当我们确定一个右括号时,假设它左边有 (a) 个左括号,右边有 (b) 个右括号,那么我们可以从 (a) 中选出 (i) 个,从 (b) 中选出 (i - 1) 个,即可组成一个合法的序列。
所以现在答案变成了,对于每个右括号:
[sum^{ileq min (a-1,b)}_{i=0} inom{a}{i+1} inom{b}{i}
]
但是我们现在只能是 (Theta(n^2)) 的复杂度,考虑将式子化简。
式子的本质含义是从 (a) 集合中选 (i+1) 个,从 (b) 集合中选 (i) 个,由:
[inom{b}{i} = inom{b}{b - i}
]
可得,问题又转化成了 (a) 集合中选 (i+1) 个,从 (b) 集合中选 (b-i) 个,那直接将 (a,b) 集合合并,直接转化成从 (a+b) 中选 (b+1) 个:
[inom{a+b}{b+1}
]
莫得了...
代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
typedef long long ll;
using namespace std;
const int maxn = 4e5 + 50, INF = 0x3f3f3f3f, mod = 1e9 + 7;
inline int read () {
register int x = 0, w = 1;
register char ch = getchar ();
for (; ch < '0' || ch > '9'; ch = getchar ()) if (ch == '-') w = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar ()) x = x * 10 + ch - '0';
return x * w;
}
inline void write (register int x) {
if (x / 10) write (x / 10);
putchar (x % 10 + '0');
}
int n, sumf[maxn], sumb[maxn];
char str[maxn];
ll ans, fac[maxn], facinv[maxn];
inline ll qpow (register ll a, register ll b) {
register ll ans = 1;
while (b) {
if (b & 1) ans = ans * a % mod;
a = a * a % mod, b >>= 1;
}
return ans;
}
inline void Init () {
fac[0] = 1;
for (register int i = 1; i <= 4e5; i ++) fac[i] = fac[i - 1] * i % mod;
facinv[400000] = qpow (fac[400000], mod - 2);
for (register int i = 4e5; i >= 1; i --) facinv[i - 1] = facinv[i] * i % mod;
}
inline ll C (register int a, register int b) {
if (a == 0 || b == 0 || a == b) return 1;
if (a < b) return 0;
return fac[a] * facinv[b] % mod * facinv[a - b] % mod;
}
int main () {
scanf ("%s", str + 1), n = strlen (str + 1), Init ();
register int tmp = 0;
for (register int i = 1; i <= n; i ++) {
if (str[i] == '(') tmp ++;
else sumf[i] = tmp;
}
tmp = 0;
for (register int i = n; i >= 1; i --) {
if (str[i] == ')') sumb[i] = tmp, tmp ++;
}
for (register int i = 1; i <= n; i ++) {
if (str[i] == '(') continue;
ans = (ans + C (sumf[i] + sumb[i], sumb[i] + 1)) % mod;
}
printf ("%lld
", ans);
return 0;
}