前言

数学符号约定:

\(\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;

题解 CF1264D1的更多相关文章

  1. 2016 华南师大ACM校赛 SCNUCPC 非官方题解

    我要举报本次校赛出题人的消极出题!!! 官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解) A. 树链剖分数据结构板题 题目大意:我 ...

  2. noip2016十连测题解

    以下代码为了阅读方便,省去以下头文件: #include <iostream> #include <stdio.h> #include <math.h> #incl ...

  3. BZOJ-2561-最小生成树 题解(最小割)

    2561: 最小生成树(题解) Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1628  Solved: 786 传送门:http://www.lyd ...

  4. Codeforces Round #353 (Div. 2) ABCDE 题解 python

    Problems     # Name     A Infinite Sequence standard input/output 1 s, 256 MB    x3509 B Restoring P ...

  5. 哈尔滨理工大学ACM全国邀请赛(网络同步赛)题解

    题目链接 提交连接:http://acm-software.hrbust.edu.cn/problemset.php?page=5 1470-1482 只做出来四道比较水的题目,还需要加强中等题的训练 ...

  6. 2016ACM青岛区域赛题解

    A.Relic Discovery_hdu5982 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Jav ...

  7. poj1399 hoj1037 Direct Visibility 题解 (宽搜)

    http://poj.org/problem?id=1399 http://acm.hit.edu.cn/hoj/problem/view?id=1037 题意: 在一个最多200*200的minec ...

  8. 网络流n题 题解

    学会了网络流,就经常闲的没事儿刷网络流--于是乎来一发题解. 1. COGS2093 花园的守护之神 题意:给定一个带权无向图,问至少删除多少条边才能使得s-t最短路的长度变长. 用Dijkstra或 ...

  9. CF100965C题解..

    求方程 \[ \begin{array}\\ \sum_{i=1}^n x_i & \equiv & a_1 \pmod{p} \\ \sum_{i=1}^n x_i^2 & ...

  10. JSOI2016R3 瞎BB题解

    题意请看absi大爷的blog http://absi2011.is-programmer.com/posts/200920.html http://absi2011.is-programmer.co ...

随机推荐

  1. ABP 的ajax请求错误:400 Empty or invalid anti forgery header token.

    ABP 的ajax请求错误 记录于2018-03-22 13:31:16 星期四 错误信息:400 Empty or invalid anti forgery header token. 我从网上找到 ...

  2. 解决: better-scroll.esm.js?f40f:180 [BScroll warn]: EventEmitter has used unknown event type: "pullingUp"

    改为这样,把所有值设为true mounted() { // 滚动条 this.scroll = new BScroll(this.$refs.wrapper, { click: true, obse ...

  3. Oracle内存管理方式介绍(转载)

    "Oracle内存管理可分为两大类,自动内存管理和手动内存管理.其中手动内存管理又可分为自动共享内存管理,手动共享内存管理,自动PGA内存管理以及手动PGA内存管理.本文会简单的介绍不同的内 ...

  4. nflsoj 5924 选排列

    与全排列略微有些不同,只需要将退出条件需要改成 u==r #include <iostream> using namespace std; const int N = 15; int r, ...

  5. C#中多线程的用法

    1.在C#中使用多线程可以使用Thread 代码例子: public class ThreadExample { public static void ThreadProc() { for (int ...

  6. P8810 [蓝桥杯 2022 国 C] 数组个数 题解

    思路比较简单的一道题. 用的五维 dp,看到二维和三维的 dp 直接膜了 orz. 正文开始. 分析 不难看出 dp. 因为 \(b_i\) 的值只与 \(a_{i-1},a_i,a_{i+1}\) ...

  7. C++ LibCurl 库的使用方法

    LibCurl是一个开源的免费的多协议数据传输开源库,该框架具备跨平台性,开源免费,并提供了包括HTTP.FTP.SMTP.POP3等协议的功能,使用libcurl可以方便地进行网络数据传输操作,如发 ...

  8. vue中添加音频和视频

    视频播放功能 1. 安装vue-video-player npm install vue-video-player --save 或 yarn add vue-video-player --save ...

  9. legend的用法

    常规使用legend的方法 1. 自动检测 设置title的label标签,随后使用不带参数的legend函数也会自动显示(但不限于一种方式设置图形的label,只要设置了图形的label,使用leg ...

  10. 纯干货!一文get昇腾Ascend C编程入门全部知识点

    本文分享自华为云社区<昇腾Ascend C编程入门教程>,作者:昇腾CANN . 2023年5月6日,在昇腾AI开发者峰会上,华为正式发布了面向算子开发场景的昇腾Ascend C编程语言. ...