CF 杂题选做
CF 杂题选做
CF2018D Max Plus Min Plus Size (Difficulty: 2200)
因为最小值错了会使答案更小,最大值错了会使答案更大,所以可以钦定最小值,这样就能保证答案的正确性。
然后考虑 DP。设 \(f_{i,0/1,0/1}\) 表示考虑前 \(i\) 个数,第 \(i\) 个是否被选择,是否钦定最大值时的最小答案。那么转移是显然的:
这样不一定会选中钦定的最小值,但不选中是一定不会更优,所以也不会影响答案的正确性。
每次枚举最小值后暴力 DP 是 \(O(n^2)\) 的,考虑优化。
考虑 DDP。对于每个点维护出对应的转移矩阵,每次就只会修改一个位置的矩阵,并查询整体的乘积。可以使用线段树维护。
于是可以优化到 \(O(4^3n\log n)\)。
CF1970E3 Trails (Hard) (Difficulty: 2200)
由于限制只能走 \(k\) 步,于是考虑矩乘。
维护出每一天的移动矩阵 \(A\),其中有 \(A_{i,j}=(s_i+l_i)\times(s_j+l_j)-l_i\times l_j\)。然后求出 \(A^k\) 的第一行的和即为答案。
这样时间复杂度是 \(O(n^3\log k)\) 的,可以通过 E2。
然后考虑将矩阵 \(A\) 表示为 \(B\times C\) 的形式,其中 \(B\) 为 \(n\times2\) 的矩阵,\(C\) 为 \(2\times n\) 的矩阵。因为有 \(A^k=(B\times C)^k=B\times(C\times B)^{k-1}\times C\),且 \(C\times B\) 为 \(2\times2\) 的矩阵,所以可以将时间复杂度优化到 \(O(2^3\log k+n^2)\)。
但还是无法通过。注意到答案只要求第一行,所以最后一次矩阵不必完整操作。转移就可以将时间复杂度优化到 \(O(2^3\log k+n)\)。
CF1970G3 Min-Fund Prison (Hard) (Difficulty: 2400)
由于边双无法被隔开,所以可以先将边双缩点,变成森林。
注意到添加的边数一定为连通块数减一,然后就只需考虑如何划分点。于是问题就转化为:有一个森林,你可以选择一棵树断掉一条边,变成两棵树。现在要把树划分为两个集合 \(S,T\),要求 \(\left|S\right|+\left|T\right|=n\),并令 \(\left|S\right|^2+\left|T\right|^2\) 最小。
考虑 DP。设 \(f_{i,j,0/1}\) 表示前 \(i\) 棵树中,有没有进行删边操作后能不能划分出一个大小为 \(j\) 的集合。转移是显然的。
注意到这可以用 bitset
优化,于是可以做到 \(O(\frac{n^2}{w})\)。
CF1868C Travel Plan (Difficulty: 2400)
由于 \(m\) 很小,于是考虑枚举最大值 \(i\) 并计算出对应的方案数 \(f(i)\)。但 \(f(i)\) 也不好直接计算,所以可以计算出最大值 \(\le i\) 的方案数 \(g(i)\),即所有在路径上的点权均 \(\le i\) 的方案数,那么有 \(f(i)=g(i)-g(i-1)\)。
然后考虑 DP,记枚举的最大值为 \(k\)。设 \(f_{i}\) 表示大小为 \(i\) 的树内的路径填法和, \(g_i\) 表示大小为 \(i\) 的树内到根的路径填法和。因为一个不在路径上的点有 \(m\) 种选法,否则有 \(k\) 种选法,则有转移方程:
然后可以用 map
记忆化。或者因为左右子树至少有一边是 \(2\) 的整次幂减一,所以可以只对 \(2\) 的整次幂进行记忆化。
时间复杂度为 \(O(m\log n)\),若用 map
就还要带上 \(O(\log\log n)\)。
CF730J Bottles (Difficulty: 1900)
别问为什么会做到 1900 的题,问就是 duel 的。
先贪心地把所有瓶子按 \(b_i\) 排序。
第一问的答案是好求的,直接扫一遍即可。于是只需考虑第二问。
考虑 DP。设 \(f_{i,j,k}\) 表示考虑前 \(i\) 个瓶子,已经使用了 \(j\) 个瓶子,已选瓶子的容积之和为 \(k\) 的最小操作次数。那么转移有
时间复杂度 \(O(n^3V)\)。
CF1845E Boxes and Balls (Difficulty: 2500)
首先,因为可以来回移动某个球,所以操作步数可以为 \(k-2t\)。
考虑设 \(f_{i,j,s}\) 表示考虑前 \(i\) 个位置,已经放了 \(j\) 个球,操作次数为 \(s\) 的方案数。显然转移方程为:
其中 \(p_j\) 表示第 \(j\) 个球的位置。
直接做是 \(O(n^2k)\) 的,不够优秀。
考虑经典方法:对每个间隙计算贡献。于是记 \(w(i)\) 表示现在的方案相较于原方案,前 \(i\) 个位置多出来的球数。那么总贡献就为 \(\sum\left|w(i)\right|\)。
于是可以设 \(f_{i,j,s}\) 表示考虑前 \(i\) 个位置,\(w(i)=j,\sum_{j\le i}\left|w(j)\right|=s\) 的方案数。转移分讨第 \(i+1\) 个位置是否放球,并求出对应的 \(w(i+1)\)。
由于 \(\sum\left|w(i)\right|\le k\),且 \(w(i)\) 每次最多变化 \(1\),所以根据等差数列求和公式有 \(w(i)\le\sqrt k\),所以时间复杂度是 \(O(nk\sqrt k)\) 的。
CF1187E Tree Painting (Difficulty: 2100)
还是 duel 的。
考虑以最初操作的节点为根。
那么第一次操作完就会分成一些子树,并且每个子树的贡献是独立的,可以分开计算。于是可以通过 DP 求出。
然后考虑换根,发现这也是简单的,可以直接维护。
CF1619G Unusual Minesweeper (Dufficulty: 2000)
duel 上瘾了。
考虑将爆炸会互相影响的点连边后一起考虑,可以使用并查集实现。而每个连通块自己爆炸的时间就为连通块内点权最小的点。
然后贪心地考虑,每次一定是点燃爆炸时间最晚的连通块。于是可以按爆炸时间排序,然后扫一遍即可。
CF748F Santa Clauses and a Soccer Championship (Difficulty: 2300)
Conclusion: 只选树的带权中心一定满足条件。
Proof:
考虑构造性证明。因为树的带权中心的任意子树的权值和均 \(\le\frac{k}{2}\)。所以可以按 dfn 序考虑每个点,每次将第 \(i\) 个点和第 \(i+\frac{k}{2}\) 个点配对,因为它们一定不在同一棵子树内。所以该结论是正确的。
CF1784D Wooden Spoon (Difficulty: 2400)
先钦定获得安慰奖的选手一定为 \(p_n\),最后只需把答案乘 \(n\) 即可。
考虑用 \(n\) 到根的路径把整棵树切开,并设从左往右第 \(i\) 棵子树的获胜者为 \(c_i\)(\(c_n=p_n\)),那么 \(c_i\) 就一定会输给 \(c_{i-1}\)。
接下来考虑已知数组 \(c\) 如何计算方案数。假设考虑到第 \(i\) 棵子树,那么第 \(i\) 棵子树就一定会被填满,即填 \(2^{n-i}-1\) 个数(减一是因为 \(c_i\) 已经确定)。在考虑还有多少个数可供选择,总共有 \(2^n\) 个数,其中 \(>c_i\) 的数有 \(2^n-c_i\) 个,还有 \(2^{n-i}\) 要留个后面的子树去填,所以方案数就为 \((2^{n-i})!\binom{2^n-c_i-2^{n-i}}{2^{n-i}-1}\)。
既然已知如何计算方案数,那么 DP 是显然的。设 \(f_{i,j}\) 表示考虑到第 \(i\) 棵子树,且 \(c_i=j\) 的方案数,那么转移方程为:
可以用前缀和优化到 \(O(n2^n)\)。
最后 \(i\) 获得安慰奖的概率就为 \(n\sum_{j<i}f_{n,j}\)。
CF1729G Cut Substrings (Difficulty: 2100)
先用 KMP 预处理出 \(t\) 在 \(s\) 中的所有出现位置,记为 \(p_1,p_2,\cdots,p_k\)。
然后考虑 DP。设 \(f_i\) 表示将前 \(p_i+m-1\) 切割开且要删掉 \([p_i,p_i+m-1]\) 的方案数,那么考虑 \(f_i\) 能贡献到那些点,记为 \(j\)。因为 \(j\) 不能影响到 \(i\),所有有 \(p_j\ge p_i+m\)。又因为 \(j\) 一定要影响到下一个未被影响的 \(k\),于是有 \(p_j<p_k+m\),其中 \(k\) 为第一个满足 \(p_k\ge p_i+m\) 的数。
然后直接转移即可。
时间复杂度 \(O(n^2)\)。
CF1622E Math Test (Difficulty: 2200)
根据经典结论,\(\left|a-b\right|=\max(a-b,b-a)\),所以可以 \(2^n\) 枚举每个人的 \(r_i\) 是与 \(a_i\) 的大小关系。
于是就可以确定每个人的 \(r_i\) 与 \(a_i\) 的正负。先把 \(a_i\) 的贡献加上,再考虑 \(r_i\) 的贡献。
直接不好做,于是考虑每道题的贡献。先计算出每道题会被计算多少次,记为 \(t_i\)。那么现在就只需确定一个排列 \(p\),使得 \(\sum p_it_i\) 最大。
根据排序不等式,可以将 \(t\) 排序,\(t\) 第 \(i\) 小的 \(p\) 只就为 \(i\)。
时间复杂度为 \(O(2^nm(n+\log m))\)。
CF144E Competition (Difficulty: 2200)
感觉挺水的,不知道怎么评到 2200 的。
从左往右以此考虑每一个点。那么每个点的目标点都构成了一段区间,每次贪心选择区间内最靠右的未被选择的点即可。可以用 set
维护。
CF2000G Call During the Journey (Difficulty: 2100)
为什么我认为这题严格大于上一题?难道是因为我 duel 输了?
考虑倒推。设 \(f_i\) 表示走到第 \(i\) 个点的最晚时间,转移有两种:
-
若 \(f_i\le t1\lor f_i-w1\ge t2\),即可以乘公交,那么有 \(f_i-w1\to f_j\)。
-
否则有两种情况。要么步行,要么等到 \(t1\) 再乘公交。那么有 \(\max(f_i-w2,t1-t1)\to f_j\)。
发现这是类最短路问题,可以使用 Dijkstra 优化。
CF1553E Permutation Shift (Difficulty: 2100)
先考虑枚举右移的位数 \(k\),那么转化为判断一个序列是否能通过不多于 \(m\) 次交换得到另一个序列。
这是经典问题。直接从初状态的第 \(i\) 位向末状态的第 \(i\) 位连边。那么最少操作数为 \(n-\) 联通块数。
这样直接做是 \(O(n^2)\) 的,考虑优化。
注意到 \(m\le\frac{n}{3}\)。而一个序列合法的必要条件是相同的位置个数多于 \(n-2\times m\ge\frac{n}{3}\),因为最多改变 \(2\times m\) 个位置。又因为每个 \(a_i\) 只有一个右移位数 \(k\) 使得右移后这一位相同,所以最多只有 \(3\) 个 \(k\) 满足条件。
时间复杂度 \(O(n)\)。
CF1582F Korney Korneevich and XOR (Difficulty: 2400)
最简单的想法是设 \(f_{i,j,k}\) 表示前 \(i\) 个数,子序列最后一个数为 \(j\),异或和为 \(k\) 是否可行,转移显然。但这样连状态数都是 \(O(nV^2)\) 的,没有前途。
考虑设 \(f_{i,j}\) 表示子序列最后一个数 \(<i\),且异或和为 \(j\) 是否可行。每次加入一个 \(a_i\) 就枚举一个满足 \(f_{a_i,j}=1\) 的 \(j\),然后令所有 \(f_{k,j\oplus a_i}\) 为 \(1\)。(其中 \(k>a_i\))
考虑优化。发现 \(f_{i,j}\) 为 \(1\) 对于 \(i\) 来讲是一段后缀,可以对于每个 \(j\) 维护出 \(mx_j\) 表示最小的 \(i\) 满足 \(f_{i,j}=1\)。那么每次就只需更新到 \(mx_{j\oplus a_i}\),并更新它。
再观察到对于一个 \(i\) 来说,满足 \(f_{i,j}=1\) 的可能会被多次更新到同一个数,且对应的 \(j\) 的数目不多。于是可以开个 vector
维护出每个 \(i\) 满足 \(f_{i,j}=1\) 的 \(j\),每次都将 \(a_i\) 对应的 vector
清空。
最后分析时间复杂度。转移是的 \(mx_i\) 是单调递减的,于是总共是 \(O(V^2)\) 的。而每个 vector
的大小最多为 \(O(V)\),所以枚举也是 \(O(V^2)\) 的。
于是最后时间复杂度为 \(O(n+V^2)\)。
CF1542D Priority Queue (Difficulty: 2200)
考虑枚举 \(p\),并计算 \(a_p\) 会在多少个子序列中计入答案。
考虑 DP,设 \(f_{i,j}\) 表示由前 \(i\) 个数构成的子序列中,小于 \(a_p\) 的有 \(j\) 个的方案数。转移需要分讨:
-
若 \(a_i\) 为
-
,那么可以从 \(f_{i-1,j}+f_{i-1,j+1}\) 转移过来。特别强调:当 \(i<p\land j=0\) 时,就算选择当前位置也可能不会导致 \(j\) 的改变,所以还可以从 \(f_{i-1,j}\) 转移过来。
-
若 \(a_i\) 为
+
且 \(a_i<a_p\),那么可以从 \(f_{i-1,j}+f_{i-1,j-1}\) zu 转移过来。 -
若 \(a_i\) 为
+
且 \(a_i>a_p\),那么可以从 \(2\times f_{i-1,j}\) 转移过来,因为选不选 \(j\) 都不变。
而当 \(a_i=a_p\) 时,可以以下标为第二关键字比较,这样就不会记重了。
时间复杂度 \(O(n^3)\)。
CF2077D Maximum Polygon
容易想到枚举最大值 \(mx\),然后用 \(\le mx\) 的数构成子序列。(这里不必保证最大值要取到 \(mx\),因为更小仍然合法)
接下来就是平凡的了。按顺序确定子序列的每一位,每次都贪心地选择较大的值,判断是否合法是容易的。
然后再用线段树上二分就可以做到 \(O(n^2\log n)\) 了。
接下来考虑一个重要结论:
若 \(\ge mx\) 的数都没有出现在子序列中,那么子序列的最大值一定 \(<\lceil\frac{mx}2\rceil\)。
考虑证明:此时如果在选上 \(mx\),左边增加的值 \(\le mx\),右边会增加 \(mx\),仍然合法。且因为字典序会变大,所以选上一定更优。
于是从大到小枚举最大值,每次做完后就把 \(\ge\lceil\frac{mx}2\rceil\) 的数都删掉然后继续做。然后最多进行 \(O(\log V)\) 轮,时间复杂度 \(O(n\log n\log V)\)。