CF785D - Anton and School - 2 | 组合数

links

给定一个只包含 () 的括号序列,求形如 (())((())) ,的子序列总数。答案取模。
\(n \leq 2 \times 10 ^ 5\)

\(O(n ^ 2)\) 的暴力显然,关键是如何计算组合数。枚举最后一个 ( ,设左边还有 \(x\)( ,右边有 \(y\)) ,则答案为

\[\sum\limits_{i = 1} ^ {\min(x + 1, y)} \binom{x}{i- 1} \binom{y}{i} = \sum\limits_{i = 1} ^ {\min(x + 1, y)} \binom{x}{x - i + 1} \binom{y}{i} = \binom{x + y}{x + 1} \]

关键是中间的那一步变形,发现下面两个数字之和为定制 \(x + 1\) ,那么这个式子的含义就可以解读为在 \(x + y\) 个球中选取 \(x + 1\) 个球的方案数,求和符号实际是在枚举在后 \(y\) 个球中选几个。

不会组合数,太逊了。

#include <iostream>
#include <cstdio>
#include <cstring>
	using namespace std;
	const int mod = 1000000007;
	const int N = 200005;
	int fac[N], ifac[N];
	string st = "";
int fpow(int x, int k) {
	int res = 1;
	while (k) {
		if (k & 1) res = (long long)res * x % mod;
		x = (long long)x * x % mod;
		k >>= 1;
	}
	return res;
}
int calc(int n, int m) {
	return (long long)fac[n] * ifac[m] % mod * ifac[n - m] % mod;
}
int main() {
	cin >> st;
	int len = st.size();
	fac[0] = 1;
	for (int i = 1; i <= len; i++)
		fac[i] = (long long)fac[i - 1] * i % mod;
	for (int i = 0; i <= len; i++)
		ifac[i] = fpow(fac[i], mod - 2);
	int cnt0 = 0, cnt1 = 0;
	for (int i = 0; i < len; i++)
		cnt1 += (st[i] == ')');
	int ans = 0;
	for (int i = 0; i < len; i++) {
		cnt1 -= (st[i] == ')');
		if (st[i] == '(') {
			ans = (ans + calc(cnt0 + cnt1, cnt0 + 1)) % mod;
			cnt0++;	
		}
	}
	printf("%d\n", ans);
	return 0;
}
posted @ 2024-03-19 16:13  kcccn  阅读(14)  评论(0)    收藏  举报