[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++