FJOI2020 的两道组合计数题
最近细品了 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)\) 的路径边权乘积的和的两倍。因此我们换个枚举方式,枚举路径上有多少条从左上到右下的转移边,就能得到
\]
其中前面部分是权值的计算,后面部分是简单的计数。首先用 \(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 的两道组合计数题的更多相关文章
- 两道相似KMP题
1.POJ 3450 Coporate Identity 这两题的解法都是枚举子串,然后匹配,像这种题目以后可以不用KMP来做,直接字符串自带的strstr函数搞定,如果字符串未出现,该函数返回NUL ...
- 又一道简单题&&Ladygod(两道思维水题)
Ladygod Time Limit: 3000/1000MS (Java/Others) Memory Limit: 65535/65535KB (Java/Others) Submit S ...
- 一道cf水题再加两道紫薯题的感悟
. 遇到一个很大的数除以另一个数时,可以尝试把这个很大的数进行,素数因子分解. . 遇到多个数的乘积与另一个数的除法时,求是否能整除,可以先求每一个数与分母的最大公约数,最后若分母数字为1,则证明可整 ...
- 算法(JAVA)----两道小小课后题
LZ最近翻了翻JAVA版的数据结构与算法,无聊之下将书中的课后题一一给做了一遍,在此给出书中课后题的答案(非标准答案,是LZ的答案,猿友们可以贡献出自己更快的算法). 1.编写一个程序解决选择问题.令 ...
- JAVA算法两道
算法(JAVA)----两道小小课后题 LZ最近翻了翻JAVA版的数据结构与算法,无聊之下将书中的课后题一一给做了一遍,在此给出书中课后题的答案(非标准答案,是LZ的答案,猿友们可以贡献出自己更快 ...
- 『ACM C++』Virtual Judge | 两道基础题 - The Architect Omar && Malek and Summer Semester
这几天一直在宿舍跑PY模型,学校的ACM寒假集训我也没去成,来学校的时候已经18号了,突然加进去也就上一天然后排位赛了,没学什么就去打怕是要被虐成渣,今天开学前一天,看到最后有一场大的排位赛,就上去试 ...
- 两道人数多,课程少,query多的题
#每天进步一点点# 来两道很相似的题目~ (智商啊智商.....) hihoCoder #1236:Scores (简单的分桶法+bitset) 2015 Beijing Online的最后一题.题目 ...
- 【T-SQL基础】01.单表查询-几道sql查询题
概述: 本系列[T-SQL基础]主要是针对T-SQL基础的总结. [T-SQL基础]01.单表查询-几道sql查询题 [T-SQL基础]02.联接查询 [T-SQL基础]03.子查询 [T-SQL基础 ...
- Linux运维跳槽必备的40道面试精华题(转)
Linux运维跳槽必备的40道面试精华题(转) 下面是一名资深Linux运维求职数十家公司总结的Linux运维面试精华,助力大家年后跳槽找个高薪好工作. 1.什么是运维?什么是游戏运维? 1)运维 ...
随机推荐
- 手摸手教你用 yapi-to-typescript生成Yapi的TypeScript数据类型
一 背景 现代社会比较重视效率,本着这个思想宗旨,能用工具自动高效做的事情,就不要低质量的勤奋.yapi-to-typescript就是一款自动生成接口请求与响应的typescript数据类型定义的工 ...
- 虚拟机研究系列-「GC本质底层机制」SafePoint的深入分析和底层原理探究指南
SafePoint前提介绍 在高度优化的现代JVM里,Safepoint有几种不同的用法.GC safepoint是最常见.大家听说得最多的,但还有deoptimization safepoint也很 ...
- 初始HTML03
**------------恢复内容开始------------** HTML 页面标签组成 一个完整的页面仅有一个html元素,在这个元素之下,包含head和body元素,前者负责说明页面结构,后者 ...
- Servlet学习一(Servlet的使用流程)
一.servlet运行流程 运行流程:浏览器发送请求到服务器,服务器根据url地址在webapps中寻找对应的项目文件夹然后再web.xml中检索对应的servlet,并进行调用二.servlet类写 ...
- 机器学习:SVM
SVM 前言:支持向量机(Support Vector Machine, SVM),作为最富盛名的机器学习算法之一,其本身是一个二元分类算法,为了更好的了解SVM,首先需要一些前提知识,例如:梯度下降 ...
- [no code][scrum meeting] Alpha 8
项目 内容 会议时间 2020-04-14 会议主题 API文档第一版交付 会议时长 30min 参会人员 PM+OCR组成员 $( "#cnblogs_post_body" ). ...
- spring cloud中使用hystrix实现回退
在微服务架构中,我们的服务被拆分成多个微服务,每个微服务完成自己的职责,微服务之间通过rpc或http进行调用.这个时候我们就要确保我们的服务高可用,但谁也说不准我们的服务能永远快速的提供服务.假如现 ...
- 攻防世界 杂项 3.神奇的Modbus
[目标] 了解modbus协议 [工具] Wireshark [分析过程] 在数据包中寻找flag就行,flag是明文形式存储. 工业设备消息传输使用modbus协议.所以我就采集了modbus的通信 ...
- 实验5:开源控制器实践——POX
一.实验目的 1.能够理解 POX 控制器的工作原理: 2.通过验证POX的forwarding.hub和forwarding.l2_learning模块,初步掌握POX控制器的使用方法: 3.能够运 ...
- 【centos】更换yum源
yum下载有时候很慢,可以换一下源: 步骤: 1)下载wget yum install -y wget 2)备份默认的yum mv /etc/yum.repos.d /etc/yum.repos.d. ...