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)运维 ...
随机推荐
- NX Open,怎样取到面的环LOOP
在封装的ufun .NET库里面,对UF_MODL_ask_face_loops这个函数并没有封装,导致我们很多不便,那我们在.NET下怎样才能使用这个函数呢??当然是手动处理一下 Public Fu ...
- 远程设备管理opendx平台搭建-appium和adb的安装
多年不见了,说起来也有3年了我又开始写博客了,这几年我还是没啥长进,还是干测试,但是测试行业的话,我已经成了一个测开了,也在搭建自己的测试网站 本系列文章讲述的是一个系列的第一部分,最终可以搭建一整套 ...
- Kali安装OWASP
我是2019版的kali,里面并没有自带OWASP工具,因为OWASP不再更新的因素,所以新版kali将它移除了 安装OWASP apt-get install zaproxy #以下都是安装软件时 ...
- Java中的函数式编程(四)方法引用method reference
写在前面 我们已经知道,lambda表达式是一个匿名函数,可以用lambda表达式来实现一个函数式接口. 很自然的,我们会想到类的方法也是函数,本质上和lambda表达式是一样的,那是否也可以用类 ...
- Spring session redis ERR unknown command 'CONFIG'
部署线上服务启动报错 redis.clients.jedis.exceptions.JedisDataException: ERR unknown command 'CONFIG' Redis CON ...
- CSP踩被记
本来想起个清新脱俗的标题,但碍于语文功底不行,于是光明正大嫖了LiBoyi的高端创意,把这篇博客命名为踩被记. Day -6 用假暴力把真正解拍没了,伤心.Rp有点低 Day -4 信息学考,\(py ...
- str数组
- hdu 2955 Robberies(背包DP)
题意: 小偷去抢银行,他母亲很担心. 他母亲希望他被抓的概率真不超过P.小偷打算去抢N个银行,每个银行有两个值Mi.Pi,Mi:抢第i个银行所获得的财产 Pi:抢第i个银行被抓的概率 求最多能抢得多少 ...
- 【java+selenium3】时间控件 (九)
1.问题描述: 在应用selenium实现web自动化时,经常会遇到处理日期控件点击问题,手工很简单,可以一个个点击日期控件选择需要的日期,但自动化执行过程中,完全复制手工这样的操作就有点难了. 如图 ...
- github上传和删除文件(三)
上传文件: git init git add * git commit -m "description" //git remote rm origin 或查看当前 git remo ...