Loading

CF 杂题选做

CF 杂题选做

CF2018D Max Plus Min Plus Size (Difficulty: 2200)

因为最小值错了会使答案更小,最大值错了会使答案更大,所以可以钦定最小值,这样就能保证答案的正确性。

然后考虑 DP。设 \(f_{i,0/1,0/1}\) 表示考虑前 \(i\) 个数,第 \(i\) 个是否被选择,是否钦定最大值时的最小答案。那么转移是显然的:

\[f_{i,0,0}=\max(f_{i-1,0,0},f_{i-1,1,0})\\ f_{i,1,0}=f_{i-1,0,0}+1\\ f_{i,0,1}=\max(f_{i-1,0,1},f_{i-1,0,1})\\ f_{i,1,1}=\max(f_{i-1,0,1}+1,f_{i-1,0,0}+a_i+1)\\ \]

这样不一定会选中钦定的最小值,但不选中是一定不会更优,所以也不会影响答案的正确性。

每次枚举最小值后暴力 DP 是 \(O(n^2)\) 的,考虑优化。

考虑 DDP。对于每个点维护出对应的转移矩阵,每次就只会修改一个位置的矩阵,并查询整体的乘积。可以使用线段树维护。

于是可以优化到 \(O(4^3n\log n)\)

Code

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)\)

Code

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})\)

Code

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\) 种选法,则有转移方程:

\[g_i=k(m^{ls+rs}+m^{rs}g_{ls}+m^{ls}g_{rs})\\ f_i=m^{rs+1}f_{ls}+m^{ls+1}f_{rs}+kg_{ls}g_{rs}+g_i\\ \]

然后可以用 map 记忆化。或者因为左右子树至少有一边是 \(2\) 的整次幂减一,所以可以只对 \(2\) 的整次幂进行记忆化。

时间复杂度为 \(O(m\log n)\),若用 map 就还要带上 \(O(\log\log n)\)

Code

CF730J Bottles (Difficulty: 1900)

别问为什么会做到 1900 的题,问就是 duel 的。

先贪心地把所有瓶子按 \(b_i\) 排序。

第一问的答案是好求的,直接扫一遍即可。于是只需考虑第二问。

考虑 DP。设 \(f_{i,j,k}\) 表示考虑前 \(i\) 个瓶子,已经使用了 \(j\) 个瓶子,已选瓶子的容积之和为 \(k\) 的最小操作次数。那么转移有

\[f_{i,j,k}=\min(f_{i-1,j,k}+a_i,f_{i-1,j-1,k-b_i}) \]

时间复杂度 \(O(n^3V)\)

Code

CF1845E Boxes and Balls (Difficulty: 2500)

首先,因为可以来回移动某个球,所以操作步数可以为 \(k-2t\)

考虑设 \(f_{i,j,s}\) 表示考虑前 \(i\) 个位置,已经放了 \(j\) 个球,操作次数为 \(s\) 的方案数。显然转移方程为:

\[f_{i,j,s}=f_{i-1,j,s}+f_{i-1,j-1,s+(i-p_j)} \]

其中 \(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)\) 的。

Code

CF1187E Tree Painting (Difficulty: 2100)

还是 duel 的。

考虑以最初操作的节点为根。

那么第一次操作完就会分成一些子树,并且每个子树的贡献是独立的,可以分开计算。于是可以通过 DP 求出。

然后考虑换根,发现这也是简单的,可以直接维护。

Code

CF1619G Unusual Minesweeper (Dufficulty: 2000)

duel 上瘾了。

考虑将爆炸会互相影响的点连边后一起考虑,可以使用并查集实现。而每个连通块自己爆炸的时间就为连通块内点权最小的点。

然后贪心地考虑,每次一定是点燃爆炸时间最晚的连通块。于是可以按爆炸时间排序,然后扫一遍即可。

Code

CF748F Santa Clauses and a Soccer Championship (Difficulty: 2300)

Conclusion: 只选树的带权中心一定满足条件。

Proof:

考虑构造性证明。因为树的带权中心的任意子树的权值和均 \(\le\frac{k}{2}\)。所以可以按 dfn 序考虑每个点,每次将第 \(i\) 个点和第 \(i+\frac{k}{2}\) 个点配对,因为它们一定不在同一棵子树内。所以该结论是正确的。

Code

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\) 的方案数,那么转移方程为:

\[f_{i,j}=\sum_{k<j}f_{i-1,k}\times\binom{2^n-c_i-2^{n-i}}{2^{n-i}-1} \]

可以用前缀和优化到 \(O(n2^n)\)

最后 \(i\) 获得安慰奖的概率就为 \(n\sum_{j<i}f_{n,j}\)

Code

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)\)

Code

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))\)

Code

CF144E Competition (Difficulty: 2200)

感觉挺水的,不知道怎么评到 2200 的。

从左往右以此考虑每一个点。那么每个点的目标点都构成了一段区间,每次贪心选择区间内最靠右的未被选择的点即可。可以用 set 维护。

Code

CF2000G Call During the Journey (Difficulty: 2100)

为什么我认为这题严格大于上一题?难道是因为我 duel 输了?

考虑倒推。设 \(f_i\) 表示走到第 \(i\) 个点的最晚时间,转移有两种:

  1. \(f_i\le t1\lor f_i-w1\ge t2\),即可以乘公交,那么有 \(f_i-w1\to f_j\)

  2. 否则有两种情况。要么步行,要么等到 \(t1\) 再乘公交。那么有 \(\max(f_i-w2,t1-t1)\to f_j\)

发现这是类最短路问题,可以使用 Dijkstra 优化。

Code

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)\)

Code

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)\)

Code

CF1542D Priority Queue (Difficulty: 2200)

考虑枚举 \(p\),并计算 \(a_p\) 会在多少个子序列中计入答案。

考虑 DP,设 \(f_{i,j}\) 表示由前 \(i\) 个数构成的子序列中,小于 \(a_p\) 的有 \(j\) 个的方案数。转移需要分讨:

  1. \(a_i\)-,那么可以从 \(f_{i-1,j}+f_{i-1,j+1}\) 转移过来。

    特别强调:当 \(i<p\land j=0\) 时,就算选择当前位置也可能不会导致 \(j\) 的改变,所以还可以从 \(f_{i-1,j}\) 转移过来。

  2. \(a_i\)+\(a_i<a_p\),那么可以从 \(f_{i-1,j}+f_{i-1,j-1}\) zu 转移过来。

  3. \(a_i\)+\(a_i>a_p\),那么可以从 \(2\times f_{i-1,j}\) 转移过来,因为选不选 \(j\) 都不变。

而当 \(a_i=a_p\) 时,可以以下标为第二关键字比较,这样就不会记重了。

时间复杂度 \(O(n^3)\)

Code

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)\)

posted @ 2025-02-07 22:18  LIUIR  阅读(43)  评论(0)    收藏  举报