CF1264D1/2 Beautiful Bracket Sequence (easy/hard version)

rgw2010發表於2024-08-21

這篇題解相對於其它題解對小白要友好一些。

模擬賽題,賽時 sb 了,\(n^2\) 都不會。

思路:

考慮什麼情況下深度最大,容易發現 (((...))) 是肯定不劣的。

那麼考慮列舉中心點的位置,設左邊有 \(a\) 個左括號和 \(x\) 個問號,右邊有 \(b\) 個右括號和 \(y\) 個問號,然後我們來列舉深度 \(i\),求 \(i\) 的貢獻次數。

要使得深度為 \(i\),則要左邊新添 \(i-a\) 個左括號,右邊新添 \(i-b\) 個右括號,直接組合數計算貢獻:

\[\sum_{i=\min(a,b)}^{\min(a+x,b+y)} \binom{x}{i-a} \binom{y}{i-b} i \]

這樣直接算是 \(O(N^2)\) 的,可以透過弱化版。

int main(){
    init();
    scanf("%s",s+1);
    n=strlen(s+1);
    For(i,1,n){
        if(s[i]==')')
          b++;
        if(s[i]=='?')
          y++;
    }
    For(i,1,n){
        if(s[i]=='(')
          a++;
        if(s[i]=='?')
          x++,y--;
        if(s[i]==')')
          b--;
        l=min(a,b),r=min(a+x,b+y);
        For(j,l,r)
          ans=Add(ans,C(x,j-a)*C(y,j-b)%mod*j%mod);
    }
    write(ans);
	return 0;
}

考慮拆式子為:

\[\Big(\sum_{i=0}^{n} \binom{x}{i-a} \binom{y}{i-b} (i-b) \Big)+ \Big(\sum_{i=0}^{n} \binom{x}{i-a} \binom{y}{i-b} b \Big) \]

先看右邊的式子,可以用範德蒙德卷積

範德蒙德卷積基本形式:

\[\binom{n+m}{k} = \sum_{i=0}^k \binom{m}{i} \binom{n}{k-i} \]

證明:
考慮組合意義,在 \(n+m\) 個物品中選 \(k\) 的方案數,是等價於在 \(n\) 個物品中選 \(i\) 個且在 \(m\) 個物品中選 \(k-i\) 個的總方案和的。

現在對於右邊的式子:

\[b \sum_{i=0}^{n} \binom{x}{i-a} \binom{y}{i-b} = b \sum_{i=0}^{n} \binom{x}{x+a-i} \binom{y}{i-b} \]

考慮組合意義後可化為:

\[b \binom{x+y}{x+a-b} \]

現在看左邊的式子:

\[\sum_{i=0}^{n} \binom{x}{i-a} \binom{y}{i-b} (i-b) \]

注意到:

\[\begin{aligned} \binom{n}{m} m &= \frac{n!m}{m!(n-m)!} \\ &= \frac{n!}{(m-1)!(n-m)!} \\ &= \frac{(n-1)!}{(m-1)!(n-m)!} n \\ &= \binom{n-1}{m-1} n \end{aligned} \]

那麼左邊式子可以化為:

\[\sum_{i=0}^{n} \binom{x}{i-a} \binom{y-1}{i-b-1} y = y \sum_{i=0}^{n} \binom{x}{x+a-i} \binom{y-1}{i-b-1} \]

後面一串也可以考慮組合意義化簡後得:

\[y \binom{x+y-1}{x+a-b-1} \]

則對於 \(i\) 這個位置的總貢獻為:

\[b \binom{x+y}{x+a-b} + y \binom{x+y-1}{x+a-b-1} \]

現在時間複雜度最佳化為 \(O(N + \log P)\)

需要提前預處理階乘和階乘逆元。

完整程式碼:

#include<bits/stdc++.h>
#define Add(x,y) (x+y>=mod)?(x+y-mod):(x+y)
#define lowbit(x) x&(-x)
#define pi pair<ll,ll>
#define pii pair<ll,pair<ll,ll>>
#define iip pair<pair<ll,ll>,ll>
#define ppii pair<pair<ll,ll>,pair<ll,ll>>
#define fi first
#define se second
#define full(l,r,x) for(auto it=l;it!=r;it++) (*it)=x
#define Full(a) memset(a,0,sizeof(a))
#define open(s1,s2) freopen(s1,"r",stdin),freopen(s2,"w",stdout);
#define For(i,l,r) for(int i=l;i<=r;i++)
#define _For(i,l,r) for(int i=r;i>=l;i--)
using namespace std;
typedef double db;
typedef unsigned long long ull;
typedef long long ll;
bool Begin;
const ll N=1e6+10,mod=998244353;
inline ll read(){
    ll x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-')
          f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return x*f;
}
inline void write(ll x){
	if(x<0){
		putchar('-');
		x=-x;
	}
	if(x>9)
	  write(x/10);
	putchar(x%10+'0');
}
ll ans;
ll n,l,r,a,b,x,y;
ll f[N],inv[N];
char s[N];
ll qpow(ll a,ll b){
    ll ans=1;
    while(b){
        if(b&1ll)
          ans=(ans*a)%mod;
        a=(a*a)%mod;
        b>>=1ll;
    }
    return ans;
}
void init(){
    f[0]=f[1]=1;
    For(i,2,n)
      f[i]=(f[i-1]*i)%mod;
    inv[n]=qpow(f[n],mod-2);
    _For(i,0,n-1)
      inv[i]=inv[i+1]*(i+1)%mod;
}
ll C(ll n,ll m){
    if(m>n||m<0||n<0)
      return 0;
    if(!m||m==n)
      return 1;
    return f[n]*inv[m]%mod*inv[n-m]%mod;
}
bool End;
int main(){
    scanf("%s",s+1);
    n=strlen(s+1);
    init();
    For(i,1,n){
        if(s[i]==')')
          b++;
        if(s[i]=='?')
          y++;
    }
    For(i,1,n){
        if(s[i]=='(')
          a++;
        if(s[i]=='?')
          x++,y--;
        if(s[i]==')')
          b--;
        ans=Add(ans,(b*C(x+y,x+a-b)%mod+y*C(x+y-1,x+a-b-1)%mod)%mod);
    }
    write(ans);
	return 0;
}

相關文章