题解 CF1264D1

发布时间 2023-04-29 11:19:03作者: Larry76

前言

数学符号约定:

\(\dbinom{n}{m}\):表示 \(n\)\(m\)

如非特殊说明,将会按照上述约定书写符号。

题目分析:

考虑题目的问题弱一点的版本,假设此时我们的括号序列是确定的如何求其括号匹配的最深深度。

如果你有些许 dp 基础的话,不难想到如下做法:

考虑位置 \(i\),将区间 \([1,i]\) 内的 ( 数量设为 \(a_i\),区间 \([i+1,n]\) 内的 ) 设为 \(b_i\),此时答案应该是 \(\max_{i\in [1,n]}(\min(a_i,b_i))\)

经过观察,我们发现:\(a_i\) 是不断增加的,\(b_i\) 是不断减少的。

证明的话考虑 \(a_i\) 的区间在不断的扩展,而 \(b_i\) 的区间在不断缩小,设扩展前的值为 \(a\),则当 \(a\) 向外扩展一格的时候只会遇到 (),故 \(a\) 扩展后的值 \(a'\) 只会等于 \(a\)\(a+1\),故显然 \(a_i\) 在过程中是不断增加的。

对于 \(b_i\) 的证明同上,在这里不多赘述。

考虑答案式子 \(\max_{i\in [1,n]}(\min(a_i,b_i))\),我们可以肯定\(a_i = b_i\) 的时候一定会取到最大值,注意这个条件不是必要的,即最大值的情况不一定是 \(a_i = b_i\),但当 \(a_i = b_i\) 时一定可以取到最大值,证明可以考虑木桶效应。

让我们继续考虑 \(a_i=b_i\) 对应的 \(i\) 的情况数量,发现我们有且仅有一种 \(i\) 能使得 \(a_i = b_i\),证明如下:

假设我们已经到了第一次出现 \(a_i = b_i\) 的点了,考虑 \(i\) 向下扩展一格会遇到什么,它肯定只会碰到 (),如果碰到的是 (,则此时 \(b_i\) 的值一定不变,因为它对 \(b_i\) 没有贡献,但是因为 \(a_i\) 多了一个 ( 所以值会加一。如果碰到的是 ),则此时 \(b_i\) 的值必定会发生改变。故我们可以的得出,我们的每一次扩展都会必然导致 \(a_i\)\(b_i\) 产生变化,不存在扩展之后 \(a_i\)\(b_i\) 都不产生变化的情况。故只有一种 \(i\) 能使得 \(a_i = b_i\)

现在有了上述的条件我们就能保证我们不会出现算重或算漏的情况了,考虑具体做法:

枚举 \(i\),记 \([1,i]\)( 的数量为 \(s_1\),记 \([1,i]\)? 的数量为 \(s_2\),记 \([i+1,n]\)) 的数量为 \(s_3\),记 \([i+1,n]\)? 的数量为 \(s_4\),枚举答案 \(j\),则当答案为 \(j\) 时的贡献就是 \(j \dbinom {s_2}{j-s_1} \dbinom {s_4}{j-s_3}\)

那么为什么贡献是这样的呢?或者说 \(\dbinom {s_2}{j-s_1}\)\(\dbinom {s_4}{j-s_3}\) 的意义是什么呢?根据我们推出的 \(a_i = b_i\) 的情况是最大值,考虑我们离当前答案 \(j\) 还差多少 (),然后将差的数量个问号变成 (),然后差的数量显然就是 \(j-s_1\)\(j - s_3\),而 \(\dbinom {s_2}{j-s_1}\)\(\dbinom {s_4}{j-s_3}\) 则表示的是 \(s_2\)\(s_4\) 个问号中选择 \(j-s_1\)\(j - s_3\) 的方案数(毕竟括号是无标号的)。

考虑预处理一下阶乘和 \(s_{1 \cdots 4}\),然后我们就能 \(\mathcal O (n^2)\) 的复杂度做完了这个题。

注意:

  • 你会发现我似乎没有讨论为什么这么扫为什么是合法的,即为什么我们能够保证我们有那么多的 ? 可选,实际上我们确实无法保证有那么多的 ? 可选,但是我们可以保证的是,如果没有那么多 ? 可选,则此时一定不会产生贡献,这一点在我们的组合数中也是有体现的。

    换言之,就是求组合数的时候需要判断一下它会不会是 \(0\)

  • \(0! = 1\)

代码实现

这里只给出了关键部分的代码实现,其余部分还恳请读者自己实现:

// sum1 表示 `(` 数量的前缀和
// sum2 表示 `)` 数量的前缀和
// sum3 表示 `?` 数量的前缀和
int ans = 0;
for (int i = 1; i <= n; i++) {
    int s1 = sum1[i];
    int s2 = sum3[i];
    int s3 = sum2[n] - sum2[i];
    int s4 = sum3[n] - sum3[i];
    for (int j = 0; j <= n; ++j) {
        ans = add(ans, mul(mul(C(s2, j - s1), C(s4, j - s3)), j));
    }
}
cout << ans << endl;