简单的括号序列




自我感觉是一个签到题,但是想了很久,最后求助才能苟活。

按照它的定义,很容易想到如何求出合法的序列:

当我们确定一个右括号时,假设它左边有 \(a\) 个左括号,右边有 \(b\) 个右括号,那么我们可以从 \(a\) 中选出 \(i\) 个,从 \(b\) 中选出 \(i - 1\) 个,即可组成一个合法的序列。

所以现在答案变成了,对于每个右括号:

\[\sum^{i\leq min (a-1,b)}_{i=0} \binom{a}{i+1} \binom{b}{i} \]

但是我们现在只能是 \(\Theta(n^2)\) 的复杂度,考虑将式子化简。

式子的本质含义是从 \(a\) 集合中选 \(i+1\) 个,从 \(b\) 集合中选 \(i\) 个,由:

\[\binom{b}{i} = \binom{b}{b - i} \]

可得,问题又转化成了 \(a\) 集合中选 \(i+1\) 个,从 \(b\) 集合中选 \(b-i\) 个,那直接将 \(a,b\) 集合合并,直接转化成从 \(a+b\) 中选 \(b+1\) 个:

\[\binom{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\n", ans);
	return 0;
}
posted @ 2020-12-03 08:22  Rubyonlу  阅读(117)  评论(4编辑  收藏  举报