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;
}

posted @ 2022-03-31 11:02  Bellala  阅读(27)  评论(0编辑  收藏  举报