做题总结
2024.12.22
ABC265E(Problem C)
可以想到以当前的位置和传送次数作状态,但是位置范围是 \(10^9\),所以不行。
观察题目发现只有 3 种操作,且 3 种操作顺序无关,所以可以考虑记录第一个和第二个的操作次数。
所以 \(f_{i,j,k}\) 表示当前传送 \(i\) 次,第一种和第二种操作的操作次数,可以根据前两种操作得到第三种操作的次数。
可以得到当前的位置,然后用 map
记录是否为合法位置并判断即可。
对于转移 \(f_{i, j, k} = f_{i - 1, j - 1, k} + f_{i - 1, j, k - 1} + f_{i - 1, j, k}\)。注意等式右边的 3 项要分别满足 \(j > 0\)、\(k > 0\) 和 \(i - j - k > 0\)。
最终答案即为 \(\sum_{i = 0}^n \sum_{j = 0} ^ {n - i} f_{n, i, j}\)。
这道题关键在于当前位置可以被 3 种操作表示,从而优化状态设计,类似于 P1541[NOIP2010 提高组] 乌龟棋。但是我对于数据范围不敏感,没有发现这题的特殊性,即只有 3 种操作。
CF1935C(Problem E)
很容易想到设 \(f_{i, j}\) 表示考虑了前 \(i\) 条信息,选了 \(k\) 个的最小代价。
可以发现前面的 \(\sum_{i = 1} ^ k a_i\) 无论怎么排列都一样,但是后面的 \(\sum_{i = 1} ^ k |b_i - b_{i + 1}|\) 就不同了,显然需要按从小到大排列比较优。
于是可以先以 \(b_i\) 为第一关键字排序,则 \(\sum_{i = 1} ^ k |b_i - b_{i + 1}|\) 转化为选择的最后一条消息的 \(b\) 减去选择的第一条消息的 \(b\)。然后在 \(f\) 数组里统计时,只需要统计 \(a_i\),并且在第一个消息的 \(b_i\) 时减去 \(b_i\)。
对于转移:
-
\(j = 1\) 时,\(f_{i,j} = min(f_{i - 1, j}, f_{i - 1, j - 1} + a_i - b_i)\)
-
否则,\(f_{i, j} = min(f_{i - 1, j},f_{i - 1, j - 1} + a_i)\)。
最后,如果当前 \(j \not= 0\),则当前状态 \(f_{i, j} + b_i\) 可以作为当前代价,即如果 \(f_{i, j} + b_i \le l\),则可以选 \(j\) 个人,记录最大值即可。
f[i][j] = f[i - 1][j];
if (j > 0)
{
if (j == 1)
f[i][j] = min(f[i - 1][j - 1] + a[i].x - a[i].y, f[i][j]);
else
f[i][j] = min(f[i - 1][j - 1] + a[i].x, f[i][j]);
if (f[i][j] + a[i].y <= m)
{
ans = max(ans, j);
}
}
有一个实现的细节就是,如果没有选当前的这个值,而直接沿用 \(f_{i - 1, j}\) 时,为什么不会出错?这是因为如果沿用这个值,说明这个值更小,而且这个值会在前面的某一个 \(f_{p, j}\) 出使得 \(f_{p, j} + b_p\) 比 \(f_{i, j} + b_i\) 更小,则这样更优。这一点是我没考虑到的,所以以后写代码的时候应该要仔细一点。
CF1077F1 / CF1077F2 (Problem F/G)
最开始我的想法是设 \(f_{i, j, k}\) 表示考虑到第 \(i\) 个元素,选了 \(j\) 个元素,最后选的一个元素位置为 \(k\),但是很快就发现 \(i\) 这一维用处不大,可以舍去,这样 \(f_{i, j}\) 表示选了 \(i\) 个元素,最后选的一个元素位置为 \(j\)。
考虑转移:
-
若不选当前元素,则 \(f_{i, j} = f_{i, j}\)。
-
若选当前元素,则 \(f_{i, l} = max(f_{i - 1, p} + a_l,f_{i,l})\)。\(l\) 表示当前枚举到的元素,\(p\) 表示需要枚举的上一个选的元素的位置。 \(i - k \le p \le n\)。
最后答案为 \(\max_{i = n - k + 1}^n f_{x, i}\)。有个细节在于是 \(n - k + 1\),因为如果选 \(n - k\),则 \(n - k + 1 \to n\) 之中没有一个元素被选。
如果 \(x < n \div k\),因为这是最稀疏的方案,如果还不满足输出 -1
。
虽然这题看起来很简单,但是初始化卡了我好久。最开始是因为初始化全赋值为 0,但是有些不合法状态会被计算进去,如 \(f_{k + 1,0 }\),这会被计算在答案中,所以要初始化一个更小值,但不能太小,如果太小,怕炸 long long
。还有就是边界问题,要考虑好会用到哪些状态,将要用到的赋初值,不要无脑 memset
。
然后对于 (hard version),只需要单调队列优化 dp,但是我写的是后不小心把 \(i\) 和 \(j\) 弄混了,导致错了两次。所以写代码前要三思而后写。
2025.2.16
CF1548B (Problem B)
最开始没有什么思路,没想到的是可以先考虑相邻两项。
考虑 \(a_i\) 和 \(a_{i + 1}\) 为朋友团的条件。存在一个 \(m\) 使得 \(a_i \bmod m = a_{i + 1} \bmod m\),即 \(a_i \bmod m - a_{i + 1} \bmod m = 0\)。即 \((a_i - a_{i - 1}) \bmod m = 0\)。即存在一个公因数 \(m\) 使得 \(m \ge 2\)。
于是我们设 \(d_i = |a_i - a_{i - 1}|\)。对于区间 \(l\) 到 \(r\),如果 \(\gcd(d_{l + 1}, d_{l + 2}, ... ,d_{r}) \ge 2\),那么就说明存在一个公因数 \(m\) 大于等于 2,且满足这一段区间为朋友团。
显然,若 \(l\) 到 \(r\) 为朋友团,那么 \(l\) 到 \(r - 1 (r > l)\) 也为朋友团,所以我们考虑枚举 \(l\) 二分 \(r\),用 st 表维护区间最大公因数。
时间复杂度 \(O(n \log^2 n)\)
CF863E (Problem D)
对于一台电视,考虑它什么时候无用。如果这台电视所覆盖区间,都有别的电视覆盖。
那什么时候会被别的电视覆盖呢?这一步我当时没有想到。
可以先求出每个时刻被覆盖了多少次,设当前电视机的覆盖区间为 \(l\) 到 \(r\),如果 \(l\) 到 \(r\) 中每个时刻被覆盖次数都大于 2,那么说明别的电视也可以覆盖当前区间,那么当前区间没用。
所以先对要用的所有点离散化一遍,然后做差分求出每个点的覆盖次数,最后用 st 表维护区间最小值。
时间复杂度 \(O(n \log n)\)。
2025.2.23
CF675E (Problem E)
1.显然,对于车站 \(i\),如果 \(j \le a_i\),\(p_{i, j} = 1\);如果 \(j > a_i\),那么我们会先走到 \(i + 1\) 到 \(a_i\) 中 \(a\) 最大的一个车站,设这个点为 \(g_i\)。
2.从 \(g_i\) 往后走,这样可以走得更远。
知道了这两点,还是不足以得到正解,但是看第 2 点,就可以考虑 dp,这一步没想到。
我们考虑设 \(f_i = \sum_{j = i + 1}^n p_{i, j}\)。
由于点 \(i\) 可以走到 \(i + 1\) 到 \(a_i\)。所以这些点对 \(f_i\) 的贡献为 1。然后对于 \(a_i + 1\) 到 \(n\) 的点。我们先走到 \(g_i\),再往后走,于是答案加上 \(f_{g_i} - (a_i - g_i) + (n - a_i)\)。这是因为从 \(i\) 走到 \(g_i\) 往后走最优,但是从当 \(j\) 在 \(g_i\) 到 \(i\) 之间的时候从 \(i\) 到 \(g_i\) 再到 \(j\) 不如直接从 \(i\) 到 \(j\),所以减掉 \(a_i - g_i\),再加上后面 \(a_i + 1\) 到 \(n\) 之间的所有点都要在 \(g_i\) 中转的代价 \(n - a_i\)。
最后答案即 \(\sum_{i = 1} ^ n f_i\)。
回顾思路会发现要求 \(g_i\),可以用一个 st 表维护。
时间复杂度 \(O(n \log n)\)。
2025.3.2
CF474F (Problem F)
如果当前数可以整除这段区间的所有数,那么他才会被释放。也就是如果这段区间的最大公因数等于这个数,那么他就可以被释放。
所以我们只需要维护区间最大公因数的数量和最大公因数以及区间最小值就可以了,答案就是区间总数减去最大公因数的数量即没被释放的蚂蚁。
- 但是这样需要维护三个数,常数很大。所以有一个优化,其实并不需要维护最小值,如果左儿子和右儿子的最大公因数不等于其中任意一个,那其实最大公因数的数量就是 0,对答案不作贡献。这个优化没有想到。
那么就只需要维护两个数最大公因数和最大公因数的次数。
2025.3.8
CF914D (Problem D)
由于可以改变段内的一个元素,显然我们肯定是把一个元素改为 \(x\) 是最优的。改为 \(x\) 之后就判断这个区间的最大公约数是不是 \(x\) 了。这个问题就相当于删掉一个数使得这段区间的最大公因数为 \(x\) 的倍数。
那么怎么判断这个事呢?没想到的是可以用线段树游走解决。其实上面的问题又可以转化成一个区间有多少个不是 \(x\) 倍数的数。所以我们在线段树的每一个节点上记录当前节点所代表区间的最大公因数,如果当前最大公因数是 \(x\) 的倍数,那就不用管,否则递归统计有多少个不是 \(x\) 的倍数的,一旦找到两个立刻返回。
最后判断这个不是 \(x\) 的倍数的个数是不是大于 1 就行了。
ABC292h (Problem E)
在前 \(i\) 个单位时间中如果有一次评级大于 \(B\),那么就可以不管后面的比赛。
设 t 是这个时间。条件就是 \(\frac{1}{t}(\sum_{i = 1} ^ t p_i) > B\),即 \(\sum_{i = 1} ^ t p_i > t \times B\),即 \(f(t) = \sum_{i = 1} ^ t (p_i - B) > 0\)。于是可以给所有 \(p\) 减一个 \(B\),考虑二分这个 \(t\),每次判断 \(\max_{i = 1} ^ t f(t)\) 是否大于 \(B\)。现在的问题为维护 \(\max_{i = 1} ^ t f(t)\)。
题目还有个限制就是会进行修改。所以考虑维护一颗线段树。线段树记录什么呢?维护当前区间的最大前缀 \(p\) 和当前区间的 \(p\) 的和。这个是没想到的。合并两个区间类似于最大子段和,再用线段树二分判断即可优化为单 \(\log\)。
2025.3.16
ABC309f (Problem F)
由于这个长方体可以旋转。所以考虑把内部先按从小到大的顺序排序。设这三个数从小到大依次为 \(x\)、\(y\)、\(z\)。对于两个长方体 \(a\) 和 \(b\)。判断 \(b\) 严格大于 \(a\):\(b.x\) 的比 \(a.x\) 大,且 \(b.y\) 比 \(a.y\) 大,且 \(b.z\) 比 \(a.z\) 大。可以证明这样比较一定最优。
可以先按 \(x\) 排序,然后先把小于当前 \(x\) 的长方体加进来,用树状数组查询小于当前 \(y\) 的最小的 \(z\)。如果这个 \(z\) 小于当前 \(z\) 那就合法,否则继续判断。
这题关键在于可以旋转和旋转之后的贪心策略,我没有意识到这一点。
2025.4.13
CF1420D (Problem F)
可以发现存在某个时刻这些灯都处于点亮状态就是选择的区间有交集。
若干个区间的交集必定是一个连续区间,这个区间的左端点为若干区间的左端点最大值。
所以我们可以枚举左端点,此时符合要求的区间就是包含枚举点的区间。
可以对所有区间排序,然后用优先队列维护右端点最大的区间。每次加入一个区间,并把右端点大于该枚举点的区间踢掉。如果此时剩下的区间数符合要求,就计算强制选择当前加入区间的方案数。
这题关键在于枚举左端点和用优先队列维护区间。