題目連接:https://codeforces.com/problemset/problem/785/Dc++
題解:算法
首先很好想的,若是咱們預處理出每一個 "(" 的左邊還有 $x$ 個 "(",以及右邊有 $y$ 個 ")",那麼就有式子以下:優化
① 若 $x+1 \le y$:$C_{x}^{0} C_{y}^{1} + C_{x}^{1} C_{y}^{2} + \cdots + C_{x}^{x} C_{y}^{x+1} = \sum_{i=0}^{x} C_{x}^{i} C_{y}^{i+1}$spa
② 若 $x+1 > y$:$C_{x}^{0} C_{y}^{1} + C_{x}^{1} C_{y}^{2} + \cdots + C_{x}^{y-1} C_{y}^{y} = \sum_{i=0}^{y-1} C_{x}^{i} C_{y}^{i+1}$code
而後算一下,哦喲 $O(n^2)$ 的優秀算法,GG,想了半天也不知道咋優化,看了題解才知道是「範德蒙德恆等式」:blog
$\sum_{i=0}^{r} C_{m}^{i} C_{n}^{r-i} = C_{m+n}^{r}$get
以及它的一個推導等式:it
$\sum_{i=0}^{m} C_{m}^{i} C_{n}^{r+i} = C_{m+n}^{m+r}$class
① 直接用推導等式能夠獲得:di
$\sum_{i=0}^{x} C_{x}^{i} C_{y}^{i+1} = C_{x+y}^{x+1}$
而 ② 則用範德蒙德恆等式獲得:
$\sum_{i=0}^{y-1} C_{x}^{i} C_{y}^{i+1} = \sum_{i=0}^{y-1} C_{x}^{i} C_{y}^{y-1-i} = C_{x+y}^{y-1}$
綜上,就變成了:對於每一個 "(",假設其左邊還有 $x$ 個 "(",右邊有 $y$ 個 ")",那麼對於答案的貢獻:
① 若 $x+1 \le y$,則爲 $C_{x+y}^{x+1}$
② 若 $x+1 > y$,則爲 $C_{x+y}^{y-1}$
只要預處理出階乘和階乘的逆元,那麼每次算 $C_{n}^{r}$ 就是 $O(1)$ 的。
AC代碼:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const ll mod=1e9+7; const int maxn=2e5+10; char s[maxn]; int n,x[maxn],y[maxn]; ll fpow(ll a,ll n) { ll res=1, base=a%mod; while(n) { if(n&1) res*=base, res%=mod; base*=base, base%=mod; n>>=1; } return res%mod; } ll inv(ll a){return fpow(a,mod-2);} ll fac[maxn],fac_inv[maxn]; ll C(ll n,ll r) { ll res=fac[n]; res*=fac_inv[r], res%=mod; res*=fac_inv[n-r], res%=mod; return res; } int main() { fac[0]=1, fac_inv[0]=inv(fac[0]); for(int i=1;i<maxn;i++) fac[i]=fac[i-1]*i%mod, fac_inv[i]=inv(fac[i]); scanf("%s",s+1), n=strlen(s+1); x[1]=0; for(int i=2;i<=n;i++) x[i]=x[i-1]+(s[i-1]=='('); y[n]=0; for(int i=n-1;i>0;i--) y[i]=y[i+1]+(s[i+1]==')'); //for(int i=1;i<=n;i++) printf("%d %d\n",x[i],y[i]); ll ans=0; for(int i=1;i<=n;i++) { if(s[i]!='(') continue; if(y[i]<=0) continue; if(x[i]+1<=y[i]) ans+=C(x[i]+y[i],x[i]+1), ans%=mod; else ans+=C(x[i]+y[i],y[i]-1), ans%=mod; } cout<<ans<<endl; }