贪心及其证明 专题
Exchange Arguments
通常用于排序的贪心证明,考虑任意两个元素 \(i,j\),有两种决策:按照 \(i,j\) 的顺序(\(A\))和按照 \(j,i\) 的顺序(\(B\)),尝试找出 \(i,j\) 之间满足的条件使得 \(B\) 可以满足题设条件的情况下 \(A\) 也一定可以满足,这样按照 \(A\) 顺序一定不劣。
[ICPC 2016 WF] Swap Space
容易确定一个大方向,就是先购买 \(a_i\le b_i\) 的来赚取尽可能多的空间,再购买 \(a_i>b_i\) 的。如果确定了顺序答案就是 \(\max\limits_{i=1}^n\left(\sum\limits_{j=1}^i a_j-\sum\limits_{j=1}^{i-1}b_j\right)\),运用邻项交换,考虑 \(i,i+1\) 顺序更优的条件,可以得到 \(a_i\le b_i\) 时按照 \(a_i\) 升序排序最优,\(a_i>b_i\) 时按照 \(b_i\) 降序排序最优。
AT_dp_x Tower
属于一类存在截止时间的贪心,其限制形如 \(\sum\limits_{j=1}^{i-1}w_j\le s_i\),容易推导出 \(A\) 顺序的条件为 \(sum\le s_i\wedge sum+w_i\le s_j\),\(B\) 顺序的条件为 \(sum\le s_j\wedge sum+w_j\le s_i\),假设顺序 \(B\) 满足,则导出 \(sum\le s_i-w_j<s_i\),因此 \(A\) 顺序的第一个条件自动满足,因此限制化简为 \(A: sum\le s_j-w_i, B: sum\le s_i-w_j\),因此只需要满足 \(s_j-w_i\ge s_i-w_j\),也就是 \(s_i+w_i\le s_j+w_j\),所以直接按照 \(s_i+w_i\) 升序排序即可。
经典树/图上贪心
[CCO 2021] Travelling Merchant
首先无法到达环的点是无解点,可以通过在反图上拓扑排序找到。对于剩下的点,考虑一个比较显然的 \(\mathrm{dp}\),令 \(f_u\) 表示从 \(u\) 点出发,最少需要多少钱才能一直走,对于边 \((u,v,r,p)\),可以得到转移 \(f_u\leftarrow^{\min} \max(r,f_v-p)\)。问题在于图中有环根本无法转移。考虑找一些性质,容易发现 \(f_u\) 一定不会超过此时图中的 \(r_{\max}\),考察图中 \(r\) 最大的那条边,此时这条边的转移一定形如 \(f_u\leftarrow^{\min} r_{\max}\),也就是说这条边转移的贡献并不依赖于 \(f_v\) 的值,因此可以直接更新后把这条边删掉,这样不影响转移的正确性,同时我们发现不断删边的过程中又会有一些无法到达环的点,这些点的转移也可以直接在反图上进行拓扑排序解决,于是过程就是:
- 在反图上进行拓扑排序,将不依赖环转移的点全部转移。
- 找到此时边权最大的边,用 \(r_{\max}\) 的值更新 \(f_u\) 后断掉这条边并更新点的入度,继续进行拓扑排序。
由于每条边最多只会被访问一次,时间复杂度 \(O(n+m\log m)\),瓶颈在排序。
[ARC098F] Donation
Observation. 一定在最后一次经过某个点的时候才捐钱。因为把前面的捐钱扔到后面显然不劣。
捐钱不好做,因为最后一次不好刻画,那就倒过来变成抢钱。令 \(C_u=\max(A_u-B_u,0)\),问题转化成:要求经过点 \(u\) 时经费 \(\ge C_u\),初次经过点 \(u\) 可以得到 \(B_u\) 的奖励。感受一下,其实是一个集合扩张的过程,把自己的连通块吃完之后尝试向领域扩展,问题在于如何高效刻画“沿途经费 \(\ge C_u\)”,考虑以 \(C_u\) 为点权建立 Kruskal 重构树,也就是将边权定为 \(\max(C_u,C_v)\),重构树上每个点的点权都比其子树中的点权要大。那么最优策略显然是往上走,然后吃掉祖先子树内的所有奖励。假设现在考虑到祖先节点 \(w\),若剩余钱数 \(x\ge C_w\) 则可以直接走过去,给 \(x\) 加上异侧子树的 \(\sum B_u\) 的贡献,否则需要加钱才能往上走,以 \(C_w-x\) 的代价把 \(x\) 调整上去。优化上述过程,考虑 \(x\) 合法的条件,就是对与任意祖先节点 \(w\),已经吃到的贡献 \(x+sum_w\ge C_{\mathrm{father}(w)}\),那么直接找到 \(\max\set{C_{\mathrm{father}(w)}-sum_w}\) 就是这个点的答案,容易一次 dfs 搞定。时间复杂度 \(O(m\log m)\)。
字典序贪心
UOJ 839 龙门题字
字典序考虑按位贪心,覆盖问题考虑时光倒流。
首先 \(a_i\) 的初值显然是假的,可以转化成一个覆盖 \([1,n]\) 的操作,不难发现这与原条件等价。
令 \(b_j\) 表示目前位置 \(j\) 需要被覆盖成的值,特别的 \(b_j=-1\) 表示这个位置已经被后面的操作覆盖过。那么合法当且仅当可以通过操作使得 \(\forall b_j=-1\)。不妨设最后一个进行的操作是 \(k\),令 \(cnt_k\) 表示 \([l_k,r_k]\) 中有多少个位置满足 \(b_j\ne -1\wedge b_j\ne x_{j-l_k+1}\),如果 \(cnt_k=0\) 那么操作 \(k\) 是可以被进行的,之后 \([l_k,r_k]\) 中的 \(b_i\) 全部被赋为 \(-1\),容易发现这个过程与顺序无关,每轮直接暴力枚举操作可以做到 \(O(nL)\) 判断。
考虑优化判断合法的过程,用一个队列维护 \(cnt_k=0\) 的操作集合,每次取出队头操作,擦除位置 \(j\) 时直接暴力遍历包含 \(j\) 的操作进行更新,如果 \(cnt\) 变成 \(0\) 就将其入队,这样每个操作最多被入队出队一次,而每个位置最多被擦除一次,这样分析下来判断的时间复杂度就变成了 \(O(n+L)\),而每个位置总可取值只有 \(O(n+L)\) 种,这样可以做到 \(O(L^2)\) 的时间复杂度。
考虑进一步优化,判定的时间复杂度无法做到更优了,考虑在按位贪心的过程中动态维护。假设当前已经确定前缀 \(c_1,c_2,\dots,c_{i-1}\),考虑 \(c_i\) 的取值,问题转化成能不能找到一个合法的操作 \(l_k\le i\le r_k\) 使得 \(b_i=-1\)。如果我们已经将 \(r_k<i\) 的操作全部处理完了,那么我们直接找到 \(cnt_k=0\) 的所有操作中 \(x_{i-l_k+1}\) 最大的那个,这个就是 \(c_i\) 的最优取值。之后继续更新 \(r_k\le i\) 的操作即可。时间复杂度 \(O(n+L)\)。
Other
P2751 工序安排 Job Processing
第一问是简单的,每次找到加工时间最短的机器即可,可以得到 \(A_i,B_i\) 分别表示两台机器加工零件中用时第 \(i\) 小的。对于第二问,不妨先二分答案 \(T\),这里也可以看作一个逆向思维,直接正着考虑 \(A\) 加工完的零件扔到哪个 \(B\) 比较困难,那么不妨假设所有的 \(B\) 工序都顶满了上界 \(T\),然后倒着往前寻找 \(A\) 工序进行拼接,这样两道工序能够拼接成功的条件就是 \(A\le T-B\),转化成 \(\max(A+B)\le T\),这样就一目了然了,显然让最小的 \(A\) 和最大的 \(B\) 进行匹配,于是合法条件就是 \(\max\set{A_i+B_{n-i+1}}\le T\),于是二分答案就没有必要了,答案就是 \(\max\set{A_i+B_{n-i+1}}\),容易做到一只 log。

浙公网安备 33010602011771号