贪心专题 by dx
A
基础贪心题。
分析几个性质:
- 有用的线段是一组极大的、互不包含的,且不包含任何一个 \(a_i\) 的线段的子集。并且很容易排序几次将其求出。
- 每个 \(a_i\) 经过的点的集合一定是一条线段。且最优情况下,所有线段互不相交(有公共点)。
- 设每条有用的线段左边紧邻的 \(a_i\) 为 \(Lid_j\),右边紧邻的 \(a_i\) 为 \(Rid_j\)。则 \(i\) 一定是被 \(Lid_j\) 向右移动覆盖,或者 \(Rid_j\) 向左移动覆盖。
有了几条性质,随便 DP 即可,复杂度 \(O(n \log n)\)。
B
设 \(cost_{i,j}\) 为 \(i\) 号轮胎跑了 \(j\) 次的代价。则 \(cost_{i,0} = 0\),\(cost_{i,j} = t + a_i j + \frac{j(j-1)(2j-1)}{6} b_i\)。我们要对其做 \((\max,+)\) 卷积。
如果 \(t=0\),那么 \(cost_{i,j}\) 一定是凸的,可以直接闵可夫斯基和合并。
对于 \(t\) 任意大,有一个做法:记 \(dp_{i,j}\) 为前 \(i\) 个轮胎跑了 \(j\) 轮的最小代价。发现这个转移是有决策单调性的。直接使用整体二分复杂度 \(O(nm \log n)\)。使用“二分栈”的时候,发现根本不需要二分,直接解二次方程就行,复杂度 \(O(nm)\),常数比较大所以过不去。
感觉这里直接用双指针跑十个有九个不靠谱,但是本题确实有人过了,不理解。
为啥不能闵可夫斯基和合并呢,因为 \(cost_{i,j}\) 不是凸的。但是你发现它大概是凸的,大部分时间可以闵可夫斯基和合并。
如果 \(cost_{i,j+1}-cost_{i,j} > cost_{i,1}\) 了,我们就可以贪。这样就保证了你不会先选了 \(\Delta_{i,j}\) 而没有选 \(\Delta_{i,0}\)。而在 \(j > \sqrt t\) 的时候一定就成立了。
所以你对小的情况做 DP,对大的情况跑闵和即可。这是牛的。
C 和 D
首先发现只有包含 \((1,1)\) 和 \((n,m)\) 的操作是有用的。如果只有 \((1,1)\) 的操作,考虑建立出异或差分数组,那么代价就是异或差分数组中 \(1\) 的个数。
而一次包含 \((n,m)\) 的操作本质上将 \((n,m)\)、\((x,m)\)、\((n,y)\)、\((x,y)\) 四个位置取反。
对于 F1,发现只有这四个位置全都是 \(1\) 操作一次才更优;对于 F2,发现不可能出现 \(x\) 和 \(y\) 相等的情况。而只有在除了 \((n,m)\) 以外的位置都是 \(1\) 才能更优。
发现这就是一个匹配问题(因为 \(x\) 和 \(y\) 都只会出现一次)。计算最大匹配即可。最后对 \((n,n)\) 还有一些小小的讨论,不过非常简单。
E
先考虑什么样的度数序列是有解的。则:只要度数都 \(\in [1,n-1]\) 且和为 \(2n-2\) 就一定有解。
证明考虑归纳。当 \(n \ge 3\) 的时候,一定有一个度数 \(>1\) 的点(假设度数为 \(t\)。取度数最大值,显然次大值一定 \(<n-1\)),和一个度数 \(=1\) 的点。将其合并为度数为 \(t-1\) 的一个新点。容易发现符合归纳条件。
实际上,我们每次选出两个度数并不都为 \(1\) 的点,给它连起来,就一定可行。
另一个结论:如果当前点 \(u\) 不是度数为 \(1\) 的点中权值最大的,\(v\) 是 \(u\) 能连的点中权值次大的。则 \(u\) 和 \(v\) 一定有连边。使用反证法和排序不等式易证。
考虑将所有点按照权值从大到小排列,并且重新标号。考察第一个点,如果其度数不为 \(1\),那么它会和第二个点直接连一条边;否则,从前往后找到第一个度数不是 \(1\) 的点,它会和第一个点连边。
所以,无论如何当前权值最大值一定会在连的边中出现。这也就保证了我们当前连的边是一个连通块,每次合并一定是块内的一个元素和块外的一个元素合并。
每个时刻,所有点的形态一定为:

其中红色点是我们连通块中的点,蓝色点不是。最后一个红色点之前的蓝色点度数一定为 \(1\)。
考虑下一次连边。如果红点的总度数目前不是 \(1\),我们就找到红点中权值最大的那一个,并且和当前的第一个蓝点连边;否则,往后找到第一个度数不为 \(1\) 的蓝点,和第一个度数不为 \(1\) 的红点连边。
使用队列维护:所有红点的出边;所有度数为 \(1\) 的点;所有度数不为 \(1\) 的点即可。
如果使用桶排复杂度线性。不过据说 sort 能过。
F
有两个主要的手段:
做法 1
设 \(dp_{i,j}\) 为,前 \(i\) 个数能否拼出 \(j\)。显然可以从 \(i-1\) 转移。那我们看哪些转移是 \(i\) 独有的。
对于 \(j\),如果 \(\text{MEX}(j,i) \neq \text{MEX} (j+1,i)\) 且 \(\text{MEX}(j,i) \neq \text{MEX}(j,i-1)\),那么称 \((j,i)\) 是关键的。显然我们有一种 \(O(V \times cnt_{关键})\) 的做法。
而关键对个数是什么呢。考虑固定 \(j\),\(\text{MEX}(i,j)\) 是若干值单调的连续段。所以总的关键对个数就是连续段个数的和。
而发现 \(j \to j+1\),最多合并一个连续段,以及将若干连续段进行拆分。根据势能分析,总的变化次数为 \(O(n)\)。
做法 2
\(dp_{i,j}\) 是 \(01\) 数组,而且有单调性,所以考虑交换状态和值。
设 \(dis_j\) 为:构造出 \(j\) 需要的最早的右端点。
发现 \(dis\) 的转移是完全图,而这个完全图的边具体的值和 \(dis\) 其实有关。
但是可以直接扩展 Dijkstra 算法,复杂度也是 \(O(n^2)\)。(就是一般的 Dijkstra 复杂度是 \(O(m \log m)\)(我也不会斐波那契堆)。而直接暴力实际上是 \(O(n^2)\)。所以说,对于稠密图直接暴力反而更优。)
G
考虑忽略 \((*,1)\) 的操作。由于题目保证了乘的总数 \(\le V\),所以只有 \(O(\log V)\) 个乘法操作。
显然你会把乘法扔到最后面,会把加法扔到最前面。如果直接枚举哪些乘法扔到了后面去,总的情况数为 \(O(2^{\log V}) = O(V)\)。
但是发现,如果 \(k\) 个乘法相同,你可以 \(2^k\) 枚举;但实际上,一定是一段前缀被扔到了后面去,所以是 \(k+1\) 种(这种优化爆搜信息量的手段非常牛)。据说只有 \(1 \times 10^4\) 的枚举量了。
而每次枚举显然可以二分套二分实现。复杂度 \(O(C \log^3 V)\),其中 \(C\) 是枚举量。
经常能遇到“有用的状态数在某个不大的范围内”这一种套路。实际上,仔细思考,发现不这样“爆搜”根本就做不了(比如迷宫守卫,比如这道题,比如萌熊那个 lgv 引理题——你根本不能把想要枚举的东西存起来,完全 DP 不了,就只能另寻他路——贪,或者爆搜)。本题的贪心是用来优化状态的。
H
做法和上一题有一点类似。
考虑将相邻距离 \(\le \lfloor \frac{v}{2^d} \rfloor\) 的点合并成一个连续段。那么我们可以形成一个 \(\log_2 V\) 层的树形结构。现在问题转化为:每一层都选定最多一个点,使得每个叶子都有至少一个祖先被选中。
很容易想到一个 \(O(nV^{\log_2 3})\) 的 DP:设 \(dp_{u,S}\) 为,\(u\) 的子树内,使用了 \(S\) 中的颜色,能否将其覆盖。然后还是那句话:交换两维。
看起来树上问题很难交换两维,但是我们发现:我们其实可以完全抛弃树的结构,在序列上做,让他尽可能靠后出现。
这样就是 \(O((n+V) \log V)\) 的。最后随便合并即可。
本题最关键的是发现:如果我们对一个数 \(V\) 的二进制进行状压,那么不同的状态数为 \(O(V)\)(上限是 \(2V\))。
I
怎么又是我做过的题,无语。
如果一个节点最终是叶子,我们称它是黑色的,否则为白色的。不过“根”在这里不认为是叶子,直接染成白色。最终可以直接加上其影响,并不重要。
一个必要条件是,所有黑点互不相邻。这个条件也是充分的。原因是,相邻的两个节点不可能同时成为叶子。而归纳容易证明。所以问题就变为“钦定某个点不被选中的最大独立集”。
不过甚至不用换根。你可以新开一个颜色“\(\color{orange} 奶龙色\)”,只有根会被染成“\(\color{orange} 奶龙色\)”,而且效果和白色一样。在 DP 里面再记录一下子树中是不是已经有“\(\color{orange} 奶龙色\)” 即可。
本题就是比较经典的,先找个充要条件,然后进行最优化。
J 和 K
考虑固定 \(r\) 怎么计算答案。令 \(l\) 从 \(r\) 扫到 \(1\)。如果 \(l=1\) 的时候有两个最大值,显然 \(l\) 就是最优的。否则设最大值是 \(a\) 取到的。
一个关键结论是,原序列的众数一定会在这个序列作为众数出现。证明显然。
我们需要求出:对于 \([1,r]\) 唯一众数 \(u\)和 \([1,n]\) 唯一众数相同的每个 \(r\),对于每个 \(v\),求出最小的 \(p\) 使得 \([p,r]\) 中 \(v\) 出现次数和 \(u\) 相同。
发现只需要钦定众数和另一个数出现次数相等即可。因为如果它并不是众数,可以扩展做到更优。
这样就可以随便根号分治做了。但是本题有 \(O(n \log n)\) 甚至 \(O(n)\) 的做法。下面讲一下 \(O(n)\) 的做法。
- 考虑拉出 \(\pm 1\) 序列。发现就是求 \(pre_i=pre_j\) 且 \(j-i\) 最大的 \((i,j)\)。
- 注意到 \(pre_0=0\),\(pre_n>0\),所以一定有 \(pre_i \ge 0\)。
- 如果 \(pre_j > pre_{j-1}\) 且 \(pre_j \neq 0\),一定可以将 \(j\) 往前移动。
因此可能有用的 \(pre_j\) 一定是非众数对应的点。
我们把这些关键点拉出来。然后要维护 \([0,\max pre]\) 中每个数第一次出现。
发现一定形如 \(cnt(v)\) 段,每段告诉你 \(k\) 第一次出现的位置是 \(k+c_k\)。
然后跑几遍前后缀和即可。
L
首先提出一个多项式复杂度的想法:依次判定 \((i,j)\)。从后往前扫——显然 \((i,j)\) 不能出现。如果有 \((i,k)\),那么 \(k\) 就应该在这一轮被杀掉。我们将其标记为“这一轮前一直活着”。之后如果出现了 \((k,w)\),那么再标记 \(w\) “这一轮前一直活着”。
直接做复杂度 \(O(n^2m)\)。
做法 1
来自粉兔:发现这显然就是一个 2-SAT 模型。设布尔变量 \(ok_{i,j}\) 表示 \(i\) 号火鸡在 \(j\) 时刻是否活着。然后推出限制关系。(注意你让一只火鸡突然死掉了其实问题不大,因为这样会让问条件变劣,需要死掉更多的火鸡)
发现如果操作影响不到火鸡,那么其状态一定保持不变。所以布尔变量的元素个数为 \(O(m)\)。
最终算答案的时候发现就是简单的 DAG 上可达性问题。而关心的状态实际上只有 \(O(n)\) 个,所以可以做到 \(O(\frac{nm}{w})\)。
做法 2
设一只火鸡往前扩展访问到的点的集合为 \(S_u\)。它可以用 \(O(nm)\) 的复杂度求出(注:实际上可以做到 \(O(n^2 \log n)\))。
容易发现,两只火鸡能扩展出来的集合是 \(S_u \cup S_v\)。而如果 \(S_u \cap S_v \neq \varnothing\),那么显然会发生冲突(考虑使用数学归纳法)。
这样可以做到 \(O(nm + \frac{n^3}{w})\)。
做法 3
自己想到的做法。这里尝试将处理两只火鸡的复杂度降到 \(O(n \log n)\)。这样不用任何观察的暴力就可以获得 \(O(n^3 \log n)\)(甚至拥有较小的常数),足以通过本题。不过悲伤的是,这并不能给整道题的最优复杂度带来任何优化。(降到 \(O(n^2)\) 量级)
其实非常简单。因为如果一只火鸡被扩展两次就坠机了。所以实质上有用的边不会超过 \(n\) 条。开一个堆来维护它即可。我写了,是对的。略加精细实现(可惜我没有)可以做到线性空间 \(O(n+m)\)。
M
线性规划的对偶是简单的。但是这个做法非常不严谨。想到之前模拟赛不管三七二十一直接上对偶保龄了,我就忍不住轻哼起来。
哎这种东西学了也想不出来。大概就是说,考虑把操作分为直线和跳线。观察 \(a_1\),如果 \(a_1=0\) 直接忽略,否则如果 \(a_2>0\) 优先用直线,最后用跳线。然后观察 \(2\),优先继承。如果能继承的线全部用完了,就全部用完了,开一些新的;否则,肯定有一些在这里就断了,不能往后继承了。
比如有 \(x\) 条直线,\(y\) 条跳线,有 \(k\) 条不能保留。那么显然 \(x\) 和 \(y\) 减去 \(k\),再开 \(k\) 个自由元。而如果 \(x<k\) 不太好办,这时候 \(y\) 条跳线至少有 \(k-x\) 条不能保留,先全给删掉就行。感觉最牛的就是自由元的设置。

浙公网安备 33010602011771号