贪心专题 by dx

A

基础贪心题。

分析几个性质:

  1. 有用的线段是一组极大的、互不包含的,且不包含任何一个 \(a_i\) 的线段的子集。并且很容易排序几次将其求出。
  2. 每个 \(a_i\) 经过的点的集合一定是一条线段。且最优情况下,所有线段互不相交(有公共点)。
  3. 设每条有用的线段左边紧邻的 \(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,对大的情况跑闵和即可。这是牛的。

CD

首先发现只有包含 \((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\) 的点,它会和第一个点连边。

所以,无论如何当前权值最大值一定会在连的边中出现。这也就保证了我们当前连的边是一个连通块,每次合并一定是块内的一个元素和块外的一个元素合并。

每个时刻,所有点的形态一定为:

image

其中红色点是我们连通块中的点,蓝色点不是。最后一个红色点之前的蓝色点度数一定为 \(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} 奶龙色\)” 即可。

本题就是比较经典的,先找个充要条件,然后进行最优化。

JK

考虑固定 \(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)\) 的做法。

  1. 考虑拉出 \(\pm 1\) 序列。发现就是求 \(pre_i=pre_j\)\(j-i\) 最大的 \((i,j)\)
  2. 注意到 \(pre_0=0\)\(pre_n>0\),所以一定有 \(pre_i \ge 0\)
  3. 如果 \(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\) 条不能保留,先全给删掉就行。感觉最牛的就是自由元的设置。

posted @ 2025-06-04 08:31  M2GA  阅读(26)  评论(0)    收藏  举报