排列有关 DP/计数
每年都在考,每年都不会。
可能有些转移方程有点小问题,不过不影响整体思路。
排列有关 DP 通常解决方式:
| 预定(绝对) | 插入(相对) | |
|---|---|---|
| 按下标 | 从左往右逐一确定值 | 从左往右逐一确定当前值在前缀中的排名 |
| 按值 | 从小到大逐一确定位置 | 从小到大逐一插入排列 |
依赖邻项或与下标产生关联的计数问题
可以尝试连续段 DP。
连续段 DP 一般可以将维护的连续段看做游离状态,即我们只需要知道当前状态有多少个连续段,而不在意它们具体位置。因为在连续段合并的时候通常就能够确定某些点的相对位置,而当状态内连续段数量达到下限时,所有点的真实位置也就已经确定了。
简单地,连续段 DP 也可以看做是建笛卡尔树的过程:
- 新建叶子节点。
- 合并节点。
- 成为某个节点的儿子。
CF1515E Phoenix and Computers
发现 \(i\) 在 \(i-1\) 和 \(i+1\) 都被打开的时候必定被打开。这是个邻项依赖的关系。对于连续段 DP,可以尝试定义状态函数 \(f_{i,len}\) 为手动打开了 \(i\) 台电脑,且这些电脑在原位置上构成了 \(len\) 个段的方案数。
那么数量下界是什么?对于自动打开的,因为无论如何都不能手动开,所以在手动打开所有可以开的电脑后统一自动开剩下电脑是一样的。那么对于已知真实位置的一种局面,无法再手动打开电脑当且仅当相邻两个连续段满足 \(r_i-l_i-1=1\)。也就是中间只有一台没被打开的电脑。如果连续段数量为 \(len\),那么这些空位的数量应该是 \(len-1\)。那么对于手动打开 \(i\) 台电脑的下界(其实应该说是上界,但是也可以看做没打开的电脑数量的下界)就是 \(len=n-i+1\)。所以最终答案应该是 \(\sum f_{i,n-i+1}\)。
现在考虑转移。通常地,连续段 DP 有三种转移:
- 加入新的段。
- 加在某一段的两边。
- 加在某一段的两边时与另外一个段合并。可以看做是这两个段中间只有一个空位,把空位占了自然就需要合并。
显然这题没有第三种情况。
- 加入新的段。\(f_{i,j}=j\times f_{i-1,j-1}\)。
- 加在某一段的两边。有 \(f_{i,j}=2\times j \times f_{i-1,j}\)。
时间复杂度 \(O(n^2)\)。
P7967 [COCI 2021/2022 #2] Magneti
之前写的,粘过来了。
注意到,对于相邻两个位置的磁铁 \(p_i,p_{i+1}\)。只有当 \(x_{p_{i+1}}-x_{p_i} \ge \max(r_{p_i},r_{p_{i+1}})\) 时两个磁铁才不会相互吸引。
我们把一个磁铁的排列状态极致压缩,保证 \(x_{p_{i+1}}-x_{p_i} = \max(r_{p_i},r_{p_{i+1}})\)。记这样会占用 \(k\) 个位置,那么剩下的 \(l-k\) 个位置就可以在这些磁铁之间随便插了。方案数为 \(C_{l-k}^{n}\)。那么现在我们就只需要维护出在占用 \(k\) 个位置时,极致压缩后磁铁排列的方案数。
不妨将 \(r_i\) 从小到大排序,那么将 \(i\) 插到 \(x,y\) 之间时,代价一定是 \(r_i\)。这样我们就去掉了 \(\max(r_{p_i},r_{p_{i+1}})\) 的限制。
考虑 DP。不难发现,对于第 \(i\) 个磁铁,如果我们放到前 \(i-1\) 个磁铁形成的排列的两端,则会产生 \(r_i\) 的代价。如果我们插到 \(x\) 和 \(y\) 之间,则会产生 \(r_i-\max(r_x,r_y)\) 的代价。前者维护不难,考虑如何维护后者。
这里有个很简单的做法,貌似叫连续段 DP。因为我们插中间会破坏掉原来的结构,那干脆让之前就没有这个结构。形式化的,我们将前 \(i-1\) 个磁铁分成若干个连续的段,那么 \(i\) 插到 \(x,y\) 之间就相当于是合并了两个连续段。只要我们强制让已经合并的两个磁铁之间不再有磁铁插入,就能够保证算法的正确性了。因为这个过程相当于是在倒着维护插入的过程,每次后插入的磁铁合并相邻的磁铁后,前面插入的磁铁就不可能再去破坏了。
定义状态函数 \(f_{i,j,k}\) 表示前 \(i\) 个磁铁,已经形成了 \(j\) 个连续段,且极致压缩后占用了 \(k\) 个位置的方案数。对第 \(i\) 个磁铁进行分类讨论:
- 单独形成一个连续段,有:\(f_{i,j,k}\to f_{i,j,k}+f_{i-1,j-1,k}\)。
- 拼在某个连续段的两边,有:\(f_{i,j,k}\to f_{i,j,k}+2\times j\times f_{i-1,j,k-r_i}\)。
- 将两个连续段拼起来。因为 \(r_i\) 是目前最大的,所以拼起来的代价将会是 \(2\times r_i\)。有:\(f_{i,j,k}\to f_{i,j,k}+2\times C_{j+1}^2 \times f_{i-1,j+1,k-2\times r_i}\)。
那么答案就是 \(\sum\limits_{i=0}^{l-n} f_{n,1,i}\times C_{l-i}^{n}\)。时间复杂度 \(O(n^2l)\)。
不知道哪里的题
网上看到的,链接。
题意:
给定序列 \(a_{1\dots n}\),满足 \(a_i \in \{0,1\}\)。对于 \(k=1\dots n\),求 \(1 \sim n\) 的排列 \(p\) 的数量,满足:
- \(p_n=k\)。
- 对于 \(i \in [1,n-1]\)。若 \(a_{p_i} =1\),则 \(p_i>p_{i+1}\),否则 \(p_i <p_{i+1}\)。
保证 \(1 \le n \le 5000\)。答案对大质数 \(mod\) 取模。
邻项产生依赖。
如果没有第一个限制怎么做。从 \(1\sim n\) 插入 \(i\)。对于当前局面中的一个连续段,如果要在两端加入 \(i\),则需要满足:
- \(a_i=1\)。可以放左边或右边,放右边的时候需要满足该连续段右边为 \(0\)。
- \(a_i=0\)。不能放左边,放右边的时候需要满足该连续段右边为 \(0\)。
那么可以看做一个连续段的右边必定为 \(0\),也就是说,\(a_i=1\) 只能用来合并(放左边的时候)。定义状态函数 \(f_{i,j}\) 表示加入 \([1,i]\) 后有 \(j\) 段,那么有转移方程:
- 加入新的段。条件为 \(a_i=0\)。\(f_{i,j}=j\times f_{i-1,j-1}\)。
- 加在某一段的两边。显然只能加在右边。\(f_{i,j}=j \times f_{i-1,j}\)。
- 加在某一段的两边时与另外一个段合并。条件为 \(a_i=1\)。\(f_{i,j}=2\times C_{j+1}^2 \times f_{i-1,j+1}\)。
注意到,如果一个连续段的右边抵到 \(n\) 了,那么这个段的右边实际上是可以是 \(0\) 的。类似于摩天大楼,再维护一位 \(0/1\) 表示是否存在抵到 \(n\) 的连续段即可。答案为 \(f_{n,1,1}\)。
如果有第一个限制怎么做。暴力地,\(O(n^3)\) 可以用上面的方法直接做。简单观察,维护 \([1,k-1]\) 分成 \(x\) 个连续段与 \([k+1,n]\) 分成 \(y\) 个连续段的情况。如果把前面 \(x\) 个连续段看成 \(0\),后面 \(y\) 个连续段看成 \(1\),\(k\) 这个点看成 \(2\)。那么一个合法的拼接应该长成:\((0)10101010\dots 01010(1)2\),因为同色连续必定合并。则 \(|x-y|\le 1\)。枚举 \(x\),暴力求方案数就行。维护 \(f_{i,j}\) 表示 \([1,i]\) 分成 \(j\) 个连续段的方案数,\(g_{i,j}\) 表示 \([i,n]\) 分成 \(j\) 个连续段的方案数。例如 \(x=y\) 的时候,方案数就是 \(f_{k-1,x} \times g_{k+1,y} \times 2\)。因为可以 \(0101\dots 01012\) 或者 \(1010\dots 10102\)。其它两种同理。那么枚举 \(x,y\) 的时间复杂度 \(O(n)\),枚举 \(k\) 的时间复杂度 \(O(n)\),预处理 \(f,g\) 的时间复杂度 \(O(n^2)\)。总时间复杂度 \(O(n^2)\)。
CF704B Ant Man
- \(j<i\)。\(x_i-x_j+c_i+b_j\)。
- \(j>i\)。\(x_j-x_i+d_i+a_j\)。
将 \(c_i\) 变成 \(c_i+x_i\),\(b_i\) 变成 \(b_i-x_i\),\(a_i\) 变成 \(a_i+x_i\),\(d_i\) 变成 \(d_i-x_i\)。则:
- \(j<i\)。\(c_i+b_j\)。
- \(j>i\)。\(d_i+a_j\)。
如果记访问的排列为 \(p\),那么这就是个关于 \(p_i,p_{i+1}\) 的分段函数的和的最优化问题。考虑从 \(1\sim n\) 加入 \(i\)。定义状态函数 \(f_{i,j}\) 表示前 \(i\) 个数,分成 \(j\) 个连续段的最小代价。
当 \(i\) 不是 \(s\) 或 \(e\) 时:
- 加入新的段。因为两边连的都一定是比 \(i\) 大的。所以 \(f_{i,j+1}=f_{i-1,j}+b_i+d_i\)。
- 加在某一段的两边。如果加某段的左边,那么 \(f_{i,j}=f_{i-1,j}+b_i+c_i\)。如果加某一段的右边,那么 \(f_{i,j}=f_{i-1,j}+a_i+d_i\)。
- 加在某一段的两边时与另外一个段合并。那么 \(f_{i,j}=f_{i-1,j+1}+a_i+c_i\)。
当 \(i=s\) 时:
- 加入新的段。因为是开头,所以 \(f_{i,j+1}=f_{i-1,j}+d_i\)。
- 加在某一段的两边。只能加左边,\(f_{i,j}=f_{i-1,j}+c_i\)。
- 加在某一段的两边时与另外一个段合并。不可能。
当 \(i=e\) 时:
- 加入新的段。因为是结尾,所以 \(f_{i,j+1}=f_{i-1,j}+b_i\)。
- 加在某一段的两边。只能加右边,\(f_{i,j}=f_{i-1,j}+a_i\)。
- 加在某一段的两边时与另外一个段合并。不可能。
最后答案为 \(f_{n,1}\)。时间复杂度 \(O(n^2)\)。
需要注意在 \(i=n\) 之前 \(\max(s,e)\) 之后不能合并成一段,还有一些限制比较容易。
AT_abc134_f [ABC134F] Permutation Oddness
这题和连续段真的有关系吗?
先给个比较正常的做法。注意到 \(|i-p_i|=\max(i,p_i)-\min(i,p_i)\)。那么将 \(i=1\dots n\) 看成红色小球,\(p_i=1\dots n\) 看成蓝色小球。并且将它们按照值从小到大排序。则问题就相当于求:红蓝小球配对,配对价值为排在后面的小球的值减去排在前面的小球的值,求有多少种配对方式,使得这 \(n\) 个红蓝小球对的价值和为 \(k\)。
定义状态函数 \(f_{i,x,y,s}\) 表示考虑完前 \(i\) 个球,有 \(x\) 个红色小球和 \(i\) 后面的蓝色小球配对,有 \(y\) 个蓝色小球和 \(i\) 后面的红色小球配对,且当前价值和为 \(s\) 的方案数。
- 红色小球。
- 匹配之前的蓝色小球。\(f_{i,x,y-1,s+v_i}=f_{i-1,x,y,s}\)。
- 匹配之后的蓝色小球。\(f_{i,x+1,y,s-v_i}=f_{i-1,x,y,s}\)。
- 蓝色小球。
- 匹配之前的红色小球。\(f_{i,x-1,y,s+v_i}=f_{i-1,x,y,s}\)。
- 匹配之后的红色小球。\(f_{i,x,y+1,s-v_i}=f_{i-1,x,y,s}\)。
这样做的时间复杂度 \(O(n^5)\)。已经可以过了。但是像这种配对问题通常有个简单的优化:记 \(cnt_{i,0/1}\) 表示前 \(i\) 个数中,红、蓝小球的数量。那么枚举 \(x\) 就能知道有 \(cnt_{i,0}-x\) 个红色小球已经与 \([1,i]\) 中的 \(cnt_{i,0}-x\) 个蓝色小球配对了,那么还没配对的蓝色小球数量一定满足 \(y=cnt_{i,1}-(cnt_{i,0}-x)\)。时间复杂度降至 \(O(n^4)\)。像这种延后决策的计数题后面会说。
然后是不太正常的连续段 DP。对于 \((i,p_i)\) 有关的问题,可以尝试连边 \(i \to p_i\)。那么会得到若干个置换环,每个置换环的价值为边的价值和。定义状态函数 \(f_{i,j,k}\) 表示插入 \([1,i]\) 的点后,形成了 \(j\) 个未合并的置换环与若干个已合并的置换环,且价值为 \(k\) 的方案数。那么从小到大加 \(i\),有:
- \(i\) 是一个新的环上某点。因为后面的都比 \(i\) 大,所以 \(f_{i,j+1,s-2i}=f_{i-1,j,s}\)。
- \(i\) 是一个新的环上某点,且该环是一元环。\(f_{i,j,s}=f_{i-1,j,s}\)。
- \(i\) 加在某个未合并的置换环两边。\(f_{i,j,s}=2 \times j \times f_{i-1,j,s}\)。
- \(i\) 加在某个未合并的置换环两边,且发生两个置换环的连接。\(f_{i,j-1,s+2i}=2\times C_{j}^2 \times f_{i-1,j,s}\)。
- \(i\) 加在某个未合并的置换环两边,且该置换环合并。\(f_{i,j-1,s+2i}=j \times f_{i-1,j,s}\)。
最后答案为 \(f_{n,0,k}\)。时间复杂度 \(O(n^4)\)。
P10547 [THUPC 2024 决赛] 排列游戏
和上面那题一模一样,只是 \(k=2m\)。很显然,\(O(n^4)\) 不够优秀。
根据摩天大楼的 trick,\(|i-j|\) 可以转化为 \(\sum\limits_{k=i+1}^{j} d_k\),其中 \(d_i=i-(i-1)\),即差分数组。那么只需要在每个 \(i\) 的时候额外加上 \(2j\) 就行了。时间复杂度变成 \(O(n^2m)\)。仍然不够优秀。进一步地,发现如果最后有 \(x\) 个置换环,那么价值和是 \(O(x^2)\) 级别的(构造 \((1),(2),(3),(4),\dots\))。所以置换环数量 \(O(\sqrt{m})\) 级别。那么时间复杂度降至 \(O(nm\sqrt{m})\)。
延后决策
通常是一些信息对于当前局面不产生影响,但是会对之后决策形成的局面产生影响。这个时候利用技巧记录这些可能产生影响的信息就是延后决策了。
AT_abc134_f [ABC134F] Permutation Oddness
注意到 \(|i-p_i|=\max(i,p_i)-\min(i,p_i)\)。那么将 \(i=1\dots n\) 看成红色小球,\(p_i=1\dots n\) 看成蓝色小球。并且将它们按照值从小到大排序。则问题就相当于求:红蓝小球配对,配对价值为排在后面的小球的值减去排在前面的小球的值,求有多少种配对方式,使得这 \(n\) 个红蓝小球对的价值和为 \(k\)。
定义状态函数 \(f_{i,x,y,s}\) 表示考虑完前 \(i\) 个球,有 \(x\) 个红色小球和 \(i\) 后面的蓝色小球配对,有 \(y\) 个蓝色小球和 \(i\) 后面的红色小球配对,且当前价值和为 \(s\) 的方案数。
- 红色小球。
- 匹配之前的蓝色小球。\(f_{i,x,y-1,s+v_i}=f_{i-1,x,y,s}\)。
- 匹配之后的蓝色小球。\(f_{i,x+1,y,s-v_i}=f_{i-1,x,y,s}\)。
- 蓝色小球。
- 匹配之前的红色小球。\(f_{i,x-1,y,s+v_i}=f_{i-1,x,y,s}\)。
- 匹配之后的红色小球。\(f_{i,x,y+1,s-v_i}=f_{i-1,x,y,s}\)。
这样做的时间复杂度 \(O(n^5)\)。已经可以过了。但是像这种配对问题通常有个简单的优化:记 \(cnt_{i,0/1}\) 表示前 \(i\) 个数中,红、蓝小球的数量。那么枚举 \(x\) 就能知道有 \(cnt_{i,0}-x\) 个红色小球已经与 \([1,i]\) 中的 \(cnt_{i,0}-x\) 个蓝色小球配对了,那么还没配对的蓝色小球数量一定满足 \(y=cnt_{i,1}-(cnt_{i,0}-x)\)。时间复杂度降至 \(O(n^4)\)。
AT_arc207_a [ARC207A] Affinity for Artifacts
和上面的题一模一样,不说。
P8321 『JROI-4』沈阳大街 2
求 \(\prod \min(A_i,B_{p_i})\) 的和。经典小球题。
把 \(A,B\) 排序,然后配对即可。
P14364 [CSP-S 2025] 员工招聘
求排列 \(p\) 的数量,使得 \(f(p) \ge m\)。对于 \(f(p)\),有:
- 初始拒绝人数为 \(w=0\)。
- 如果 \(s_i=0\),则拒绝的人数 \(w+1\)。
- 如果 \(s_i=1\),且 \(c_i \le w\),则拒绝的人数 \(w+1\)。
- 如果 \(s_i=1\),且 \(c_i >w\),则拒绝的人数不变。
\(f(p)\) 即为 \(n-w\)。
像这种题,发现随着天数增加,拒绝人数不降,并且需要的 \(c\) 也不降。那么就可以尝试按照天数往后 DP。
进一步地,对于第 \(i\) 天的情况分类讨论:
- \(s_i=0\)。无论 \(c_j\) 是多少都会拒绝。
- \(s_i=1\)。需要满足 \(c_j > w\) 才不会拒绝。
发现我们只关心 \(c_j\) 和 \(w\) 的大小关系(\(s_i=0\) 可以看做 \(>,=,<\) 都有),而不关心具体值。那么尝试设计状态函数 \(f_{i,w,j}\) 表示前 \(i\) 天结束,拒绝人数为 \(w\),且 \([1,i]\) 中一共有 \(j\) 个 \(>w\) 的人的方案数。这样设计的好处是?
- 能够通过枚举 \(w\) 解决拒绝人数的问题。
- 能够通过预先站位,延后决策解决后面不知道某个数能不能选的问题。
考虑转移:
- \(s_i=0\)。
- 这一天 \(c \le w\)。因为已知之前选择了 \(j\) 个 \(>w\) 的人,所以能选的人数一定为 \(\sum\limits_{i=1}^{n}[c_i \le w]-(i-1-j)\)。维护 \(s_i=\sum\limits_{j=1}^{n}[c_j \le i]\)。枚举 \([1,i-1]\) 中有多少个选了 \(c > w\) 的位置恰好是 \(w+1\),那么有:\(f_{i,w+1,j-x}=f_{i-1,w,j}\times (s_w-(i-1-j))\times C_{cnt_{w+1}}^{x} \times C_{j}^{x} \times x!\)。
- 这一天的 \(c=w+1\)。同理有 \(f_{i,w+1,j-x}=f_{i-1,w,j}\times C_{cnt_{w+1}}^{x+1} \times C_{j}^{x} \times (x+1)!\)。
- 这一天的 \(c>w+1\)。延后决策了,先不管选了什么。\(f_{i,w+1,j-x+1}=f_{i-1,w,j}\times C_{cnt_{w+1}}^{x} \times C_{j}^{x} \times x!\)。
- \(s_i=1\)。
- 这一天的 \(c \le w\)。拒绝人数会增加,\(f_{i,w+1,j-x}=f_{i-1,w,j}\times (s_{w}-(i-1-j))\times C_{cnt_{w+1}}^x \times C_{j}^{x} \times x!\)。
- 这一天的 \(c>w\)。拒绝人数不变,\(f_{i,w,j+1}=f_{i-1,w,j}\)。
对于答案。枚举最后一天结束时的 \(w\),那么有答案为:\(\sum\limits_{w=0}^{n}f_{n,w,n-s_w}\times (n-s_w)! [n-w \ge m]\)。
时间复杂度 \(O(n^4)\)。发现 \(x\) 的上界是 \(cnt_{w+1}\),那么枚举 \(i\) 时间复杂度 \(O(n)\),枚举 \(j\) 时间复杂度 \(O(n)\),枚举 \(w\) 时间复杂度 \(O(n)\),枚举 \(x\) 时间复杂度 \(O(cnt_{w+1})\)。后面两个总的时间复杂度 \(O(\sum\limits_{w=0}^{n}cnt_w)\),也就是 \(O(n)\) 的。那么这样转移就是对的。
时间复杂度 \(O(n^3)\)。
P14465 霞む夏の灯
\(c_{\min(|i-p_i|,m)}\) 和一位。
套路地,构造 \(112233\dots nn\)。点 \(i\) 有颜色,红或蓝。红点表示原有的 \(i\),蓝点表示 \(p\)。
首先如果这一位确定了,那么删掉 \(i,p_i\)。则现在问题变成:红蓝匹配,价值为 \(c_{\min(\max(u,v)-\min(u,v),m)}\)。求价值乘积的和。
因为 \(m\le 6\),也就是说我们只需要在意和 \(i\) 距离 \(<6\) 的点的状态与 \(\ge 6\) 的点被选择的数量。定义状态函数 \(f_{i,j,k,s}\) 表示前 \(i\) 个数,有 \(j\) 个红球匹配后面,\(k\) 个蓝球匹配后面与 \(\ge i+6\) 的位置匹配,\(i\) 前面 \(m\) 个数的状态为 \(s\) 的方案数。考虑加入 \(i\):
- 是红球。
- 匹配之前的蓝球。
- 匹配一个 \(i-j \le m\) 的蓝球。那么记 \(s\) 中 \(0\) 的为已经配对的,\(1\) 为没有配对的。记 \(s'\) 为去掉某个 \(s\) 中的蓝球 \(1\) 且加入一个 \(0\) 的状态,同时去掉 \(i-m-1\)。则:\(f_{i,j,k-1,s'}=f_{i-1,j,k,s}\times c_{dis}\)。
- 匹配一个 \(i-j > m\) 的蓝球。\(s'\) 为去掉 \(i-m-1\),且加入一个 \(0\) 的状态。则 \(f_{i,j,k-1,s'}=f_{i-1,j,k,s}\times c_{m}\times Cnt\)。其中 \(Cnt\) 为不在 \(s\) 中的之前的蓝球数量。
- 匹配之后的蓝球。\(s’\) 为去掉 \(i-m-1\),且加入一个 \(1\) 的状态。则 \(f_{i,j+1,k,s'}=f_{i-1,j,k,s}\)。
- 匹配之前的蓝球。
- 是蓝球。
- 匹配之前的红球。
- 匹配一个 \(i-j \le m\) 的红球。那么记 \(s\) 中 \(0\) 的为已经配对的,\(1\) 为没有配对的。记 \(s'\) 为去掉某个 \(s\) 中的红球 \(1\) 且加入一个 \(0\) 的状态,同时去掉 \(i-m-1\)。则:\(f_{i,j-1,k,s'}=f_{i-1,j,k,s}\times c_{dis}\)。
- 匹配一个 \(i-j > m\) 的红球。\(s'\) 为去掉 \(i-m-1\),且加入一个 \(0\) 的状态。则 \(f_{i,j-1,k,s'}=f_{i-1,j,k,s}\times c_{m} \times Cnt\)。其中 \(Cnt\) 为不在 \(s\) 中的之前的红球数量。
- 匹配之后的红球。\(s’\) 为去掉 \(i-m-1\),且加入一个 \(1\) 的状态。则 \(f_{i,j,k+1,s'}=f_{i-1,j,k,s}\)。
- 匹配之前的红球。
答案为 \(f_{n,0,0,0}\times \delta\)。其中 \(\delta\) 为原已经确定的位置的贡献和。时间复杂度 \(O(n^32^mm)\)。好像已经可以过了。
照搬优化方式,记 \(cnt_{i,0/1}\) 表示前 \(i\) 个数,红、蓝小球的数量。那么 \(k=cnt_{i,1}-(cnt_{i,0}-j)\)。时间复杂度 \(O(n^2 2^mm)\)。哦,因为同为 \(i\) 的有两个小球,所以 \(m=2m_0\)。
CF1608F MEX counting
点。
因为我们不在意 \(\operatorname{MEX}=k\) 时,\(>k\) 的那些数具体是什么,那么可以尝试延后决策。定义状态函数\(f_{i,j,k}\) 表示前 \(i\) 个数,\(\operatorname{MEX}([a_1,\dots,a_i])=j\),且 \(>j\) 的数有 \(k\) 个的方案数。那么考虑对 \(a_i\) 的值分类讨论:
- \(a_i <j\)。加入后对 \(\operatorname{MEX}\) 无影响,有:\(f_{i,j,k}=f_{i-1,j,k}\times j\)。
- \(a_i=j\)。加入后会让 \(\operatorname{MEX}\) 增加,且 \(\operatorname{MEX}\) 会变成第一个没出现过的值。枚举这个值为 \(j'\),且 \([j+1,j'-1]\) 中的数量为 \(x\)。则:\(f_{i,j',k-x}=f_{i-1,j,k}\times C_{k}^x \times g_{j'-j-1,x}\)。其中 \(g_{i,j}\) 表示给 \(j\) 个数涂色,每个颜色可以涂 \(i\) 种颜色,且每种颜色至少涂一次的方案数。有:\(g_{i,j}=i(g_{i,j-1}+g_{i-1,j-1})\)。
- \(a_i>j\)。加入后对 \(\operatorname{MEX}\) 无影响,有:\(f_{i,j,k}=f_{i-1,j,k}\)。
要求对于每个 \(i\),\(f_{i,j,*}=0 [|j-b_i|>k]\)。
答案就是 \(\sum\limits_{j=0}^{n+1}\sum\limits_{k=0}^{n}f_{n,j,k}\times (n-j)^k\)。时间复杂度 \(O(n^5)\)。
考虑优化。因为对于每个 \(i\),\(f_{i,j,*}=0[|j-b_i|>k]\)。所以直接枚举 \(|j-b_i|\le k\) 的位置转移的时间复杂度就变成 \(O(n^3k^2)\)。发现瓶颈在于第 \(2\) 类转移,但是好像怎么也优化不了。
那么尝试从状态定义进行优化。对于延后决策的实质,是不管对当前局面不产生影响的位置的具体值。发现对于 \([1,i]\) 中两个数 \(A_{j},A_k\)。如果 \(A_j=A_k\),那么本质上后面那个数是不会对所有局面产生任何影响的。所以可以尝试定义状态函数 \(f_{i,j,k}\) 表示,前面 \(i\) 个数,\(\operatorname{MEX}([a_1,\dots,a_i])=j\),且 \([1,i]\) 中比 \(j\) 大的数有 \(k\) 种的方案数。对 \(a_i\) 的值分类讨论:
- \(a_i<j\)。加入后对 \(\operatorname{MEX}\) 不产生影响。\(f_{i,j,k}=j \times f_{i-1,j,k}\)。
- \(a_i=j\)。加入后对 \(\operatorname{MEX}\) 产生影响。枚举现在的值 \(j'\)。因为第三维状态是记录不同的值的数量,所以有:\(f_{i,j',k-(j'-j-1)}=f_{i-1,j,k}\times A_{k}^{j'-j-1}\)。
- \(a_i>j\)。
- \(a_i\) 这个值在之前没出现过。\(f_{i,j,k+1}=f_{i-1,j,k}\)。
- \(a_i\) 这个值在之前出现过了,那么 \(i\) 不会对任何局面产生影响,\(f_{i,j,k}=k\times f_{i-1,j,k}\)。
最后答案就是 \(\sum\limits_{j=0}^{n+1}\sum\limits_{k=0}^{n}f_{n,j,k}\times A_{n-j}^k\)。根据上面的优化,只维护有用信息,时间复杂度 \(O(n^2k^2)\)。对于第 \(2\) 类转移:
换一下,有:
发现 \(j'+(k+(j-j'-1))=k+j-1\)。维护 \(g_{i,x,y}=\sum f_{i-1,j,k}\times k![j+k=y\land j \le x]\)。那么 \(f_{i,j,k}=\frac{1}{k!}g_{i-1,j-1,(j+k)-1}\)。
对于 \(g_{i,j,k}\),先把 \(f_{i,j,k}\times k!\) 加到 \(g_{i,j,j+k}\) 上,再搞个前缀和 \(g_{i,j,k}=g_{i,j,k}+g_{i,j-1,k}\) 就行了。时间复杂度 \(O(n^2k)\)。
由此,延后决策不光能做排列,还能做一般。(其实 AT_arc207_a 就可以看出来)
卡常,注意常数优化。
P8863 「KDOI-03」构造数组
直观感受,\(i\) 变成 \(b_i\) 需要 \(b_i\) 次操作,那么可以看做小球匹配问题。
注意到 \(\sum b_i \le 3\times 10^4\)。对于每个 \(i\),建立 \(b_i\) 个颜色为 \(i\) 的小球,那么问题变成:每次可以选择两个颜色不同的小球匹配,求不同匹配顺序的方案数。
直接做会算重,因为 \((x,y),(x,y)\) 在交换后等价。考虑方案数为:\(\frac{(\sum\limits_{i=1}^{V}cnt_i)!}{\sum\limits_{i=1}^{V}cnt_i!}\)。我们直接维护的是分子,现在需要对每种情况维护分母。
那么考虑那小球去填空,而不是匹配。也就是说,维护到 \(i\) 时,有多少个位置已经成对了,有多少个位置有 \(1\) 个,有多少个位置是空的。那么如果存在两个位置 \((0,y),(0,y)\)。同时拿 \(x\) 去填,就不会有问题了,因为我们不在意这两个 \(x\) 填的顺序或时间。
定义状态函数 \(f_{i,j,k}\) 表示前 \(i\) 个数填完,有 \(j\) 个位置有 \(1\) 个,有 \(k\) 个空位的方案数。那么对于 \(i\):枚举 \(x\) 个与有一个的位置成对,\(y\) 个填空位。则:\(f_{i,j-x+y,k-y}=C_{j}^{x}\times C_{k}^{y}\times f_{i-1,j,k}\),因为 \(x+y=b_i\),所以:\(f_{i,j-x+b_i-x,k-b_i+x}=C_{j}^{x}\times C_{k}^{b_i-x}\times f_{i-1,j,k}\)。时间复杂度 \(O(\frac{(\sum b_i) ^3}{4})\)。
依旧套路优化,$k= \frac{\sum b_i}{2}-j-\frac{(\sum\limits_{x<i}^{}b_x)-j}{2} $。时间复杂度 \(O(\frac{(\sum b_i)^2}{2})\)。
笛卡尔树上 DP
也就是枚举最大值,以最大值作为断点将排列划分成两部分。如果这两部分之间不产生依赖关系,那么问题可以变成两个本质相同的子问题,再通过组合数合并。
P9084 [PA 2018] Skwarki
首先发现最后剩下的数一定是 \(n\)。
把笛卡尔树建出来,模拟一下。发现本质是每次同时删去只有不超过一个儿子的点。简单地想,由于笛卡尔树上 \(\operatorname{LCA}(l,r)\) 是 \([l,r]\) 的最大值,那么当 \(u\) 只有一个儿子时,它和它父亲一定相邻。而父亲又比它大,所以会被删。注意笛卡尔树删一次后不一定仍然是树,需要再合并。
因为树根最后被删,所以尝试枚举最大值的位置。那么这个时候序列 \([1,n]\) 就被分成了 \([1,x-1]\) 和 \([x+1,n]\)。注意到存在左右边界的情况,那么定义状态函数 \(f_{i,j,0/1,0/1}\) 表示将 \(1\dots i\) 的排列删完,需要 \(j\) 次操作,且该排列的左边、右边是否是边界的方案数。注意到一个排列不可能同时包含左、右边界,所以可以去掉第二维(因为最后枚举 \(n\) 的位置时一定会把 \([1,n]\) 分成两半)。
考虑最后一次是怎样删的:
- 左儿子删空的操作次数与右儿子删空的操作次数相同,那么此时需要额外 \(1\) 的代价将根删除。
- 左儿子删空的操作次数与右儿子删空的操作次数不同,当次数小的那边被删完后,根一定会在下一步被删除。
那么有转移方程:
对于有边界的情况,如果 \(j1 \ne j2\),那么在 \(j1 < j2\) 的时候根会先变成端点,所以需要等到另一个儿子删完后再删除。
对于答案,有:\(\sum\limits_{i=2}^{n-1}f_{i-1,x,1}\times f_{n-i,y,1}\times \frac{(n-1)!}{(i-1)!(n-i-1)!}[\max(x,y)=k]+2\times f_{n-1,k,1}\)。
发现每次至少删掉 \(\frac{n}{2}\) 个点,所以第二维是 \(O(\log n)\) 级别的。那么时间复杂度 \(O(n^2\log^2 n)\)。
CF1580B Mathematics Curriculum
笛卡尔树上两点 \(\operatorname{LCA}\) 为区间最值。
那么 \(x\) 是好的当且仅当 \(dep_x=m\)。问题转化为:求排列 \(p\) 的数量,满足该排列对应的笛卡尔树中恰好有 \(k\) 个深度为 \(m\) 的点。
定义状态函数 \(f_{i,j,k}\) 表示大小为 \(i\) 的笛卡尔树中,深度为 \(j\) 的点有 \(k\) 个的方案数。那么将 \(i\) 插入排列,有:
- \(f_{i,1,1}=x!\times (i-x-1)!\times \frac{(i-1)!}{(x)!(i-x-1)!}\)。
- \(f_{i,j+1,k1+k2}=f_{x,j,k1}\times f_{i-x-1,j,k2}\times \frac{(i-1)!}{(x)!(i-x-1)!}\)。
答案为 \(f_{n,m,k}\)。时间复杂度 \(O(n^5)\),考虑优化。
直接卷可以做到 \(O(n^4\log n)\)。但是发现如果深度为 \(x\) 的点的数量不超过节点数的一半,卡一下好像就能过。
在 \(f_{x,j,k1}=0\) 时不转移就行了,稳定 700ms 内。
贡献提前计算
CF1437F Emotional Fishermen
求排列数量,要求:对于任意 \(i=1\dots n\),记 \(mx_i=\max\limits_{j=1}^{i}a_{p_j}\),则 \(a_{p_i}\ge 2mx_{i-1} \lor 2a_{p_i} \le mx_{i-1}\)。
发现 \(mx_i \ge mx_{i-1}\),且在 \(a_{p_i}\ge 2mx_{i-1}\) 时 \(mx_i=a_{p_i}\),在 \(2a_{p_i} \le mx_{i-1}\) 时 \(mx_i =mx_{i-1}\)。
如果已知排列 \(p\),那么可以将序列划分成若干段,每段都满足:
- \(a_{p_{l_i}}\ge 2a_{p_{l_i-1}}\)。
- \(2\max\limits_{j=l_i+1}^{r_i}a_{p_j} \le a_{p_{l_i}}\)。
也就是说,我们对前缀 \(\max\) 分段。
枚举前缀 \(\max\) 为 \(a_i\)。定义状态函数 \(f_{i,j}\) 表示前缀 \(\max\) 是 \(a_i\),且 \(2a_k \le a_i\) 的 \(k\) 中有 \(j\) 个数没选择的方案数。枚举上一个前缀 \(\max\),那么有 \(cnt_i-cnt_{lst}+j-1\) 个数。那么:\(f_{i,cnt_i-cnt_{lst}+j-1-k}=f_{lst,j}\times A_{cnt_i-cnt_{lst}+j-1}^{k}\)。
这里发现贡献延后不太好优化,因为时间复杂度稳定 \(O(n^4)\)。
观察转移,发现当 \(a_i \ge 2a_{lst}\) 时,任意 \(2a_k \le a_{lst}\) 一定有 \(2a_k \le a_i\),也就是说,\(k\) 这个下标只要不是前缀 \(\max\),就一定可以放在第一个 \(2a_k \le a_i\) 后面的值作为前缀 \(\max\) 的段中。考虑贡献提前计算,那么有:\(f_i=\sum f_{lst}\times A_{n-1-cnt_{lst}-1}^{cnt_i-cnt_{lst}-1}\)。时间复杂度 \(O(n^2)\)。
可以简单优化到 \(O(n)\)。
根据排列中元素的相对顺序插入值
其实上面有一些题也是这个思路。下面的题就纯暴力了。
AT_abc282_g [ABC282G] Similar Permutation
求排列 \(p1,p2\) 的数量,使得 \(p1_i,p2_i\) 与 \(p1_{i+1},p2_{i+1}\) 偏序情况相同的数量恰好为 \(k\)。因为我们不在意每个位置具体是什么数,类似于 AT_dp_t,尝试通过插入的方式维护。具体地,对于 \([1,i-1]\) 的排列和 \([1,i]\) 的排列,如果 \(p_i=j\),那么在 \([1,i-1]\) 的排列中的体现是将所有 \(p_k \ge j\) 的数增加 \(1\),从而保证之前的排列中不存在 \(j\) 这个值。例如:
可以保证,这样做对于每个 \([1,i]\) 的排列都有一个一一对应的 \([1,i-1]\) 的排列。也可以想象成将 \(p_n\) 删掉后,比它大的都应该减 \(1\) 来使得值域在 \([1,i-1]\) 且两两不同。
定义状态函数 \(f_{i,x,a,b}\) 表示前 \(i\) 个数,匹配的和为 \(x\),且 \(p1_i=a,p2_i=b\) 的方案数。那么有:
前缀和优化做到 \(O(n^4)\)。
主观感受,这种按照相对顺序插入的做法不需要考虑排列是否会出现相同元素,通常可以在邻项产生偏序关系时使用。一般比连续段 DP 更好写。需要支持后缀或前缀整体加时无影响,即两个数的关系不随真实值变化而变化,只依赖相对大小。
AT_agc043_d [AGC043D] Merge Triplets
对于排列中连续 \(4\) 个数 \(p_{l},p_{l+1},p_{l+2},p_{l+3}\)。若 \(p_l>p_{l+1}>p_{l+2}>p_{l+3}\),显然不合法。因为只能将 \(3\) 个数分在一组,而无论怎么分都会有一个比 \(p_l\) 小。又因为连续,所以一定是一个栈或多个栈的栈顶。那么一定对。
则现在我们按照前缀 \(\max\) 划分,就可以得到若干个长度不大于 \(3\) 的段。需要将一些 \(1\) 的段和 \(2\) 的段合并,凑成刚好 \(n\) 个长度为 \(3\) 的段。显然,不能两个长度为 \(2\) 的段合并。则当长度为 \(1\) 的段的数量不小于长度为 \(2\) 的段的数量时,将 \(2\) 消耗完后一定能构造合法;且不满足时一定会至少剩下两个长度为 \(2\) 的段,一定不能构造合法。
那么一个排列合法,当且仅当:按照前缀 \(\max\) 划分后,所有段的长度 \(\le 3\),且长度为 \(1\) 的段的数量不小于长度为 \(2\) 的段的数量。
尝试按照相对大小确定排列。因为只在意 \(1\) 的数量和 \(2\) 的数量的大小关系,尝试定义状态函数 \(f_{i,j}\) 表示确定了 \(p_1\dots p_i\) 的相对大小,且 \(1\) 的数量减 \(2\) 的数量为 \(j\) 的方案数。那么:
- 加一个长度为 \(1\) 的段。那么 \(i+1\) 的相对大小一定是 \(i+1\),\(f_{i+1,j+1}=f_{i,j}\)。
- 加一个长度为 \(2\) 的段。在 \(i+1\) 是最大的情况下,\(i+2\) 的大小不能大于 \(i+1\) 的值。\(f_{i+2,j-1}=(j+1)\times f_{i,j}\)。
- 加一个长度为 \(3\) 的段。同上,有 \(f_{i+3,j}=(j+2)\times (j-1)\times f_{i,j}\)。
最后答案就是 \(\sum\limits_{j\ge 0}^{}f_{n,j}\)。时间复杂度 \(O(n^2)\)。
其它
CF1806D DSU Master
考虑 \((i,i+1,a_i)\) 在什么条件下会对答案贡献 \(1\)。
- 如果 \(a_i=0\)。那么 \(find(i+1)\) 会连向 \(find(i)\)。此时只有在 \(find(i)=1\) 时才会有贡献,也就是说需要满足 \(\max\limits_{j=1}^{i-1}tim_j < tim_i\) 且 \(find(i)=1\)。
- 如果 \(a_i=1\)。那么 \(find(i)\) 会连向 \(find(i+1)\)。此时不可能产生贡献。
对于 \(\max\limits_{j=1}^{i-1}tim_j < tim_i\),方案数为 \(\frac{k!}{i-1}\)。对于 \(find(i)=1\),发现当 \(a_{i-1}=0\) 时,\(p(i)=p(i-1)\);当 \(a_{i-1}=1\) 时,\(p(i)=(1-\frac{1}{i-1})p(i-1)\)。因为 \(a_{i-1}\) 是 \(1\) 的时候,一定要前面 \(tim\) 的最大值不大于 \(i-1\) 的 \(tim\)。时间复杂度 \(O(n)\)。
好像和排列计数没关系。
P4678 [FJWC2018] 全排列
如果已知 \(P_1,P_2\),就是求有多少个区间 \([l,r]\),满足 \(P_{1,l}\dots P_{1,r}\) 的逆序对数量不超过 \(E\),且 \(P_{1,i}\) 和 \(P_{2,i}\) 的相对大小一样。直观地,就是离散化后相同。
那么根据离散化相同的限制,就可以得到:\((P_{1,i},P_{1,j})\) 与 \((P_{2,i},P_{2,j})\) 偏序情况相同。
其实好像没必要,既然离散化了,不妨记 \(A=[P'_{1,l}\dots P'_{1,r}]\)。则 \(A\) 一定是 \(1\dots r-l+1\) 的排列。那么该区间会对所有离散化后为 \(A\) 的 \((P_1,P_2)\) 产生贡献。方案数为:\((C_{n}^{r-l+1}\times (n-(r-l+1))!)^2\)。
那么就很简单了,维护 \(f_i\) 表示所有 \(1\dots i\) 的排列中,逆序对数量不超过 \(E\) 的数量。则答案等于:\(\sum\limits_{i=1}^{n}f_i \times (C_{n}^{i}\times (n-i)!)^2\times (n-i+1)\)。定义状态函数 \(f_{i,j}\) 为 \(1\dots i\) 的排列中,逆序对数量为 \(j\) 的方案数。从小到大加入 \(i\),因为 \([1,i-1]\) 都比 \(i\) 小,所以如果 \(i\) 排在位置 \(j\),则会对逆序对数量产生 \((i-j)\) 的贡献,有:\(f_{i,j}=\sum\limits_{x=0}^{i-1}f_{i-1,j-x}\)。时间复杂度 \(O(nE)\)。
P2606 [ZJOI2010] 排列计数
\(i\) 和 $\lfloor i/2 \rfloor $ 连边。显然是棵二叉树,需要满足 \(u\) 比 \(u\) 子树内所有点小。维护 \(f_{u}\) 表示 \(u\) 为根子树内点的相对大小满足条件的方案数。那么合并左右子树可以看做可重集排列的合并,因为各个子树相对顺序一定了。则:\(f_{u}=f_{ls_u}\times f_{rs_u}\times \frac{(siz_u-1)!}{(siz_{ls_u})!(siz_{rs_r})!}\)。时间复杂度 \(O(n)\)。
注意 \(m\) 可能小于 \(n\)。
AT_agc054_b [AGC054B] Greedy Division
困难。
如果第一个人选择得到橘子编号的顺序为 \(p_{1,1}\dots p_{1,m}\),第二个人选择得到的橘子编号的顺序为 \(p_{2,1}\dots p_{2,n-m}\)。
发现,在已知 \(p_1,p_2\) 的情况下,\(p\) 是唯一的。有:
- 如果 \(sA \le sB\),那么只能在 \(p_i\) 放 \(p_{1,*}\) 中的一个数,且一定是第一个没放的。
- 如果 \(sA >sB\),同理只能放唯一的一个数。
- 因为最初 \(sA=sB\),所以 \(p_1\) 也是确定的。
那么问题变成,维护集合 \(s\) 的数量,使得 \(\sum\limits_{i\in s}^{} w_i=\sum\limits_{i\notin s}^{} w_i\)。背包即可,时间复杂度 \(O(n^2V)\)。

浙公网安备 33010602011771号