[CSP-S模拟测试]:简单的括号序列(组合数)

题目传送门(内部题82)


输入格式

  一行一个字符串$ss$,保证$ss$中只包含$'('$和$')'$。


输出格式

  一行一个整数,表示满足要求的子序列数对$10^9+7$的结果。


样例

样例输入1:

)(()()

样例输出1:

6

样例输入2:

()()()

样例输出2:

7

样例输入3:

)))

样例输出3:

0


数据范围与提示

样例解释:

  第一组样例中,有以下几种子序列满足条件(字符串下标从$1$计数):
  删除$1,5$位置的字符,得到$(())$
  删除$1,2,3,4$位置的字符,得到$()$
  删除$1,2,4,5$位置的字符,得到$()$
  删除$1,2,5,6$位置的字符,得到$()$
  删除$1,3,4,5$位置的字符,得到$()$
  删除$1,3,5,6$位置的字符,得到$()$

数据范围:

  设$n$为$ss$长度
  对于$20\%$的数据,$n\leqslant 20$
  对于$50\%$的数据,$n\leqslant 2,000$
  对于$100\%$的数据,$n\leqslant 200,000$


题解

可以从左到右枚举左右括号的分界点,但是显然会算重,考虑容斥?

其实有更简单的做法,直接要求旁边的左(右)括号必须选就好了。

然后就有了$n^2$的做法。

但是如果你做过这道题:排列组合,那就简单多了。

时间复杂度:$\Theta(n)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
const int mod=1000000007;
int n;
char ch[200001];
int l[200001],r[200001];
long long fac[200001],inv[200001];
long long ans;
long long qpow(long long x,long long y)
{
	long long res=1;
	while(y)
	{
		if(y&1)res=res*x%mod;
		x=x*x%mod;
		y>>=1;
	}
	return res;
}
void pre_work()
{
	fac[0]=1;
	for(int i=1;i<=200000;i++)
		fac[i]=fac[i-1]*i%mod;
	inv[200000]=qpow(fac[200000],mod-2);
	for(int i=200000;i;i--)
		inv[i-1]=inv[i]*i%mod;
}
long long C(long long x,long long y)
{
	if(x<y)return 0;
	return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
int main()
{
	pre_work();
	scanf("%s",ch+1);
	n=strlen(ch+1);
	for(int i=1;i<=n;i++)l[i]=l[i-1]+(ch[i]=='(');
	for(int i=n;i;i--)r[i]=r[i+1]+(ch[i]==')');
	for(int i=1;i<=n;i++)
		if(ch[i]=='(')
			ans=(ans+C(l[i-1]+r[i+1],r[i+1]-1))%mod;
	printf("%lld",ans);
	return 0;
}

rp++

posted @ 2019-10-24 07:46  HEOI-动动  阅读(213)  评论(0编辑  收藏  举报