题目传送门(内部题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++
原文地址:https://www.cnblogs.com/wzc521/p/11730090.html
时间: 2024-10-09 15:41:05