1. 程式人生 > 其它 >cf629 C. Famil Door and Brackets

cf629 C. Famil Door and Brackets

題意:

給定長為 m 的括號序列,在序列的左右兩邊各加一些括號,擴充套件成長為 n 的合法括號序列,求方案數。

\(1\le m\le n\le 1e5,n-m\le2000\),注意 n 不一定是偶數

思路:

似乎可以嗯算,但是看到 n-m 只有 2000,所以當然是列舉+dp更方便啦!

眾所周知,一個括號序列合法有兩個條件:左括號總數等於右括號總數,且任意字首中的左括號數大於等於右括號數。

列舉在左邊共放 \(i\)\((\)\(j\)\()\),則 \(i\ge j\)。對於給定串 \(s\) 的中每個位置 \(p\),應有 \(i+precnt_( [p] \ge j+precnt_) [p]\implies i-j\ge \max\limits_p \{ precnt_)-precnt_( \}\)

知道了左邊放幾個,可以推出右邊放幾個。從右往左看,應有 \(j'-i'\ge \max\limits_p \{ sufcnt_(-sufcnt_) \}\)

預處理上述 \(\max\)

dp預處理放 \(i\)\((\)\(j\)\()\) 的方案數 \(f(i,j)\)\(f(i,j)=f(i-1,j)+f(i,j-1)\)

媽的說了這麼多,其實就是個簡單列舉

const signed N = 1e5 + 3, M = 2003, mod = 1e9 + 7;
ll n, m, f[M][M]; char str[N];

signed main() {
    f[0][0] = 1; //dp預處理
    for(int i = 0; i < M; i++)
        for(int j = 0; j <= i; j++) {
            if(i) f[i][j] += f[i-1][j];
            if(j) f[i][j] += f[i][j-1];
            f[i][j] %= mod;
        }

    iofast;
    cin >> n >> m >> str + 1;

    if(n % 2) return cout << 0, 0; //特判奇數

    int lef = 0, rig = 0; //s中的左/右括號總數
    for(int i = 1; i <= m; i++)
        if(str[i] == '(') lef++; else rig++;

    int llim = 0, rlim = 0; //算一下限制
    for(int i = 1, s = 0; i <= m; i++) {
        if(str[i] == ')') s++; else s--;
        llim = max(llim, s);
    }
    for(int i = m, s = 0; i; i--) {
        if(str[i] == '(') s++; else s--;
        rlim = max(rlim, s);
    }

    ll ans = 0;
    for(int i = 0; i + lef <= n/2; i++)
        for(int j = 0; j + rig <= n/2 && i - j >= llim; j++) {
            int ii = n/2 - i - lef, jj = n/2 - j - rig;
            if(jj - ii >= rlim)
                (ans += f[i][j] * f[jj][ii]) %= mod;
        }
    cout << ans;
}