最近细品了 FJOI2020 的两道计数题,感觉抛开数据范围不清还卡常不谈里面的组合计数技巧还是挺不错的。由于这两道题都基于卡特兰数的拓展,所以我们把它们一并研究掉。

首先是 D1T3 ,先给出简要题意:

  • 有 \(4\) 个栈 \(s_{1 \ldots 4}\),初始时 \(s_1\) 从栈底到栈顶为 \(1,2, \ldots, n\),\(s_{2 \ldots 4}\) 为空。
  • 接下来,对它们进行若干次操作,每次操作可以任选 \(i \in \{1,2,3\}\),并将 \(s_i\) 的栈顶弹出并压入到 \(s_{i+1}\) 中。
  • 要求任意时刻 \(s_2\) 从栈底到栈顶单调递减,\(s_3\) 从栈底到栈顶单调递增,同时全部操作结束后所有元素都被移入 \(s_4\) 中。
  • 求出全部操作结束后不可能出现在 \(s_4\) 中的排列个数,对 \(m\) 取模。
  • 多组询问, \(n\le 5\times 10^5\) 。

是否感觉到十分困难啊(如果秒了的话请自行节约时间)?那么我们还是从简单情况开始考虑。如果 \(n=1,2,3\) 的话那答案当然是 \(0\) 。 \(n=4\) 呢?借助计算机打表,可以发现只有两种情况不合法,分别是 \(P_1=1,4,2,3\) 和 \(P_2=2,4,1,3\) 。再强行打出 \(n=5\) 的表,我们可以惊奇地发现,每一个不合法的排列都存在一个长度为 \(4\) 的子序列满足将其离散化之后为 \(P_1\) 或 \(P_2\) 。

虽然之后我们很快就打不动表了,但我们可以大胆猜想:排列不合法是否等价于其存在一个离散化后为 \(P_1\) 或 \(P_2\) 的长为 \(4\) 的子序列?

为此,我们需要探究 “ \(P_1\) 或 \(P_2\) ”这一约束更直观的意义。可以发现它就相当于约束 “小,大,小,中” 不存在于排列之中。接着我们可以尝试使用归纳法:

对于一个满足约束的结果排列 \(Q\) ,找到其中 \(n\) 对应的位置。如果 \(n\) 在开头那么我们先把它移到栈 \(4\) 之后就变成了 \(n-1\) 的情况。

否则的话,我们利用约束条件,找到 \(n\) 之前的最小值 \(k\) ,把序列划分为 \(3\) 个部分: \(n\) 之前的一个部分, \(n\) 及 \(n\) 之后大于 \(k\) 的数一个部分, \(n\) 之后小于 \(k\) 的数一个部分。根据约束条件,第 \(2\) 部分的元素必然都在第 \(3\) 部分的元素之前(否则子序列 \(k,n,3x,2y\) 不合法),且 \(2\) 部分的元素单调递降(否则找到一个上升位 \(2x,2y\) ,子序列 \(k,n,2x,2y\) 不合法)。

\(1,3\) 部的元素个数都小于 \(n\) ,根据归纳法我们可以对其寻得一组操作序列。接着我们考虑拼出 \(n\) 的操作序列。对于 \(1\) 的操作序列,使其所有弹 \(2,3\) 栈的操作不变,在弹 \(1\) 栈时顺便把该元素上方的未弹元素弹出,进行完此段操作之后我们就得到了第 \(1\) 部分且 \(2\) 栈中的序列与第 \(2\) 部分相同, \(3\) 栈为空。我们先把 \(2\) 栈中的所有的元素弹到 \(3\) 栈再把 \(3\) 栈中的所有元素弹到 \(4\) 栈,就得到了第 \(2\) 部分,最后再把第 \(3\) 部分的操作序列与之拼接即可。于是我们得到了 \(n\) 的操作序列。

而必要性是显然的,如果我们把存在此子序列的排列移动成功了,那么单纯考虑对子序列的移动操作,把子序列换成离散化之后的值,就马上得到了 \(Q_1\) 或 \(Q_2\) 的合法移动方法,这显然是不可能的。

那么我们就能证明排列合法等价于其不存在“小,大,小,中”子序列。接下来考虑计数。可以尝试先容斥成所有合法排列的个数,再按值从小到大进行增量。对于加入 \(i\) 前的序列,可以发现加入 \(i\) 等价于使 \(i\) 前面的任意两个元素之间都无法插入元素且其余位置仍都可以插入元素,那么这就相当于把 \(i\) 前面的所有元素合并成一个元素。那么我们可以直观地设计出一个 DP : \(f_{i,j}\) 表示加入前 \(i\) 个数后有 \(j\) 个元素的方案数。那么边界条件 \(f_{1,1}=1\) ,枚举加入前有几个元素可得 \(f_{i,j}=2f_{i-1,j-1}+\sum_{k\ge j}f_{i-1,k}=2f_{i-1,j-1}+f_{i,j+1}-f_{i-1,j}\) ,那么我们就可以进行 \(O(n^2)\) 的 DP ,最后答案就是 \(\sum_j f_{n,j}\) 。

怎么优化呢?我们自然还是考虑先打表找规律,可以发现 \(n\) 的答案就是 \(f_{n+1,2}\) (注意边界 \(1\) 要特判)。其实这是因为对于一个 \(n\) 的方案案在其末尾加入 \(n+1\) 后就能构造与 \(f_{n+1,2}\) 的一一对应。

接下来就是喜闻乐见的二维平面建模了。我们把转移式放到二维平面上,把状态当成点,转移当成有向边,观察一下答案的组合意义。可以发现它就是所有 \((2,2)\) 到 \((n+1,2)\) 的路径边权乘积的和的两倍。因此我们换个枚举方式,枚举路径上有多少条从左上到右下的转移边,就能得到

\[Ans=2\sum_{i=0}^{n-1} 2^i(-1)^{n-1-i}\binom{n-1+i}{2i}C_i
\]

其中前面部分是权值的计算,后面部分是简单的计数。首先用 \(C_i\) 即卡特兰数算出斜右下转移边与向左转移边的拼接方案数,再用组合数算出向下转移边与其它转移边的拼接方案数,相乘即得到所有方案数。

至此我们假装 \(m\) 是大质数,就有了个 \(O(n)\) 的算法。交到 cfcs 的算法在线平台上你会收获一个 TLE ,这是因为这个式子常数属实巨大。把组合数与卡特兰数展开对消简化一下式子再交上去,多试几次就差不多能过了。是不是除了最后的卡常部分和模数未知部分都很不错呢?

然后是 D2T2 ,这题虽然也神仙,但在 D1T3 面前还是略显拉跨的。简要题意:求把一个凸多边形划分为 \(n(n\le 10^6)\) 个边数为 \(k(3\le k\le 200)\) 的凸多边形的方案数。多组询问,只需输出所有询问答案之和,对 \(10^9+7\) 取模。

可以发现 \(m=3\) 就是简单的卡特兰数,注意把大组合数转为下降幂除以阶乘计算即可。

那么我们如何推广呢?可以考虑利用卡特兰数中常见的折线表示。首先思考怎么表示一种方案。

我们从一个点开始按逆时针顺序加边走到下一个点(相当于向后的高度变化量为 \(+1\) 的折线),一条连接当前点与之前点的边就会划分出一个多边形(相当于删去 \(m-1\) 条边并加入一条边,也即一条向后的高度变化量为 \(-m+2\) 的折线)。

按这样的顺序做完就把一个多边形划分唯一对应到了一条折线,由于加入第一条边之后任何时刻都至少有一条边没被删去,所以折线除第一个点外高度都大于 \(0\) 。同时满足这个条件的折线都能对应到一个多边形划分方案,那么我们就建立了多边形划分与合法折线的一一对应,所以划分的个数就是合法折线的条数。

那么合法折线的个数如何计算呢?我们不妨考虑所有 \(\binom{nm-n+1}{n}\) 条有 \(n\) 个下降线的折线。如果将一条折线从最后一个最低点断开做循环移位,那么它刚好对应到一条合法折线。同时,一条合法折线所有 \(nm-n+1\) 个循环移位恰对应 \(nm-n+1\) 条不同的折线。又由于对应关系可逆,所以合法折线的 \(nm-n+1\) 倍就是 \(\binom{nm-n+1}{n}\) ,那么其个数也就十分明朗了。

FJOI2020 的两道组合计数题的更多相关文章

  1. 两道相似KMP题

    1.POJ 3450 Coporate Identity 这两题的解法都是枚举子串,然后匹配,像这种题目以后可以不用KMP来做,直接字符串自带的strstr函数搞定,如果字符串未出现,该函数返回NUL ...

  2. 又一道简单题&&Ladygod(两道思维水题)

    Ladygod Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others) Submit S ...

  3. 一道cf水题再加两道紫薯题的感悟

    . 遇到一个很大的数除以另一个数时,可以尝试把这个很大的数进行,素数因子分解. . 遇到多个数的乘积与另一个数的除法时,求是否能整除,可以先求每一个数与分母的最大公约数,最后若分母数字为1,则证明可整 ...

  4. 算法(JAVA)----两道小小课后题

    LZ最近翻了翻JAVA版的数据结构与算法,无聊之下将书中的课后题一一给做了一遍,在此给出书中课后题的答案(非标准答案,是LZ的答案,猿友们可以贡献出自己更快的算法). 1.编写一个程序解决选择问题.令 ...

  5. JAVA算法两道

    算法(JAVA)----两道小小课后题   LZ最近翻了翻JAVA版的数据结构与算法,无聊之下将书中的课后题一一给做了一遍,在此给出书中课后题的答案(非标准答案,是LZ的答案,猿友们可以贡献出自己更快 ...

  6. 『ACM C++』Virtual Judge | 两道基础题 - The Architect Omar && Malek and Summer Semester

    这几天一直在宿舍跑PY模型,学校的ACM寒假集训我也没去成,来学校的时候已经18号了,突然加进去也就上一天然后排位赛了,没学什么就去打怕是要被虐成渣,今天开学前一天,看到最后有一场大的排位赛,就上去试 ...

  7. 两道人数多,课程少,query多的题

    #每天进步一点点# 来两道很相似的题目~ (智商啊智商.....) hihoCoder #1236:Scores (简单的分桶法+bitset) 2015 Beijing Online的最后一题.题目 ...

  8. 【T-SQL基础】01.单表查询-几道sql查询题

    概述: 本系列[T-SQL基础]主要是针对T-SQL基础的总结. [T-SQL基础]01.单表查询-几道sql查询题 [T-SQL基础]02.联接查询 [T-SQL基础]03.子查询 [T-SQL基础 ...

  9. Linux运维跳槽必备的40道面试精华题(转)

    Linux运维跳槽必备的40道面试精华题(转)   下面是一名资深Linux运维求职数十家公司总结的Linux运维面试精华,助力大家年后跳槽找个高薪好工作. 1.什么是运维?什么是游戏运维? 1)运维 ...

随机推荐

  1. MySQL:基础语法-1

    MySQL:基础语法-1 记录一下 MySQL 基础的一些语法,便于查询,该部分内容主要是参考:bilibili 上 黑马程序员 的课程而做的笔记,由于时间有点久了,课程地址忘记了 关于数据库的安装操 ...

  2. BUAA软件工程个人作业-软件案例分析

    BUAA个人博客作业-软件案例分析 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 软件案例分析 我在这个课程的目标是 学习开发软件的能力 这个作 ...

  3. CICD 流水线就该这么玩系列之一

    今天给大家分享的是 DevOps 世界中非常流行的一个 GitOps 工具 - Argo CD.如果你还不知道什么是 GitOps,欢迎留言告诉我,根据热度,我会再写一篇详细讲解 GitOps 的文章 ...

  4. Spring Security Jwt Token 自动刷新

    token的自动刷新 一.功能需求 二.功能分析 1.token 的生成 2.token 的自动延长 3.系统资源的保护 4.用户如何传递 token 三.实现思路 1.生成 token 和 refr ...

  5. 色彩滤镜矩阵(Color Filter Array)

    数码相机上的每个象素都带有一个光感应器,用以测量光线的明亮程度.由于光电二极管是只支持单颜色的装置,它不能区别不同波长的光线.因此,数码相机工程师在相机感应器的上部装上了一套镶嵌式的颜色滤镜,一个颜色 ...

  6. 架构师之路-redis集群解析

    引子 上篇<架构师之路-https底层原理>里我提到了上面的整体视图,文章也介绍了想要真正能在工作中及时正确解决问题的基本功:原理理解透彻.今天以redis集群解析为例介绍一个及时敏锐的发 ...

  7. AXI协议中的模棱两可的含义的解释(Cachable和Bufferable)

    转载:https://blog.csdn.net/hit_shaoqi/article/details/53243173 Cachable和Bufferable 一个Master发出一个读写的requ ...

  8. C# 如何将日期格式化ISO8601模式

    类似于这样的时间戳格式:预计来访时间,时间参数需满足ISO8601格式:yyyy-MM-ddTHH:mm:ss+当前时区,例如北京时间:2018-07-26T15:00:00 + 08:00 stri ...

  9. VUE项目实现主题切换

    需求是 做一个深色主题和浅色主题切换的效果 方法一 多套css 这个方法也是最简单,也是最无聊的. <!-- 中心 --> <template> 动态获取父级class名称,进 ...

  10. 更改mysql数据库根目录

    1,查看原根目录 2,然后关闭数据库服务 3,cp -r 源根目录到目的根目录 4,修改my.cnf文件定义的根目录位置到目的根目录 5,启动数据库