凸贪心杂题

P11328

贪心策略的发现的通用方法:假定最优解集合,再微调研究

很明显我们应该使用邻项交换法,假设选定了按照什么顺序参加比赛,并且参加的每一场都获得了徽章,那么考虑先操作 \(i\) 再操作 \(j\) 合法,而先操作 \(j\) 非法的条件是:

\[now\le L_i,now+X_i\le L_j,now\le L_j,now+X_j>L_i \]

首先,必然有 \(now\le L_i,now\le L_j\),只需要看剩下的条件:

\[L_i-X_j<now\le L_j-X_i\implies L_i+X_i<L_j+X_j \]

因此按 \(L+X\) 升序操作最优。

然后,考虑获得最多的徽章,这是一个经典问题。

从头开始扫,如果可以直接用 \(L_i\),那么直接用,否则找到有效的比赛里 \(X\) 最大的扔掉,换成 \(X_i\)(如果 \(X_i\) 更小)。

证明是容易的,假设换了 \(j\),也就有:

\[L_j+X_j\le L_i+X_i,X_j>X_i\implies L_j<L_i \]

那么将 \(j\) 同时刻替换为 \(i\) 不会变差,一定合法。

JOISC2018 J

很经典的贪心结论,需要注意的是一定要设 \(0,n+1\)\(\infty\),否则不能满足每次至少增加一个的条件。

P14265

枚举一下首尾颜色就变成了上个题,方案输出可以通过区间 \(\oplus 1\) 打标记实现。

注意细节。

CF1799 F

显然需要先除再减,对同一个数字。

不妨设用了除减,除法,减法,没用的数字个数分别是 \(a,b,c,d\),那有:

\[a+b+c+d=n,a+b=k_1,a+c=k_2 \]

并且,同一类操作用数字较大的一定比数字小的更好,因此我们就知道了 \(a,d\) 分别取数字从大到小排序后的前缀和后缀。

不妨枚举 \(a\)

那么考虑单个除法和减法怎么分?

对于 \(val>2b\) 而言,除法不劣于减法,而对于 \(val<2b\) 而言都是除法劣势。

因此可以知道,我们会首先做除法,然后在一个分界点做减法,最后剩下的数字做除法。

事实上最优分界点可以算出来,大概可以做到线性或者一个 \(\log\),但这里 \(n\) 只有 \(5000\),暴力枚举,借助前缀和即可 \(O(1)\) 计算。

AT-jag2017autumn_j

首先可以找到一些性质(虽然没有用)。

如果我一个都不生产,一定找人要,如果我只生产一个,一定自己用,如果我生产两个,一定支援他人。

因此这是个最小费用流问题,由此原问题具有凸性。

暴力建图是 \(O(n^2)\) 的图,但是可以做前后缀优化建图,图就只有 \(O(n)\) 条边了。

这启发我们做 \(dp\)

不妨设 \(dp_{i,j}\) 为处理了 \([1,i]\),当前的流量为 \(j\)(正负号代表流向)。

转移就是:

\[dp_{i,j}=\max(dp_{i-1,j+1}+|j+1|d_i,dp_{i-1,j}+|j|d_i+g_i,dp_{i-1,j-1}+|j-1|d_i+2g_i) \]

这是在做什么?
先做一个 \(dp'_{j}=dp_{j}+|j|d_i\),然后再往右平移一格,然后就变成了与 \(\lbrace 0,g_i,2g_i\rbrace\)\((\max,+)\) 卷积。

那么可以考虑维护闵可夫斯基和,也就是维护 \(dp\) 值的差分,显然这是下凸壳。

我们需要做的?

  1. 维护最低点,也就是 \(0\) 的所在,支持平移。
  2. 支持 \(+|j|d_i\),也就是正负分离,\(\le0\)\(>0\) 分开。
  3. 动态插入,维护有序结构

可以使用对顶堆,那么几个操作分别是:

  1. 往右平移一格:从\(\le 0\) 的堆里找最大值扔进 \(>0\) 的堆。
  2. \(|j|d_i\),就是一个打标记的事情,维护左右堆分别的标记即可,左减右加。
  3. 动态插入,直接扔进堆里再平衡就行了,记得扔两次。

最低点的值是好维护的。

CF436 E

考虑 \(i\to i+1\),有四个策略:

  1. \(0\to 1\)
  2. \(1\to 2\)
  3. \(2\to 1,0\to 2\)
  4. \(1\to 0,0\to 2\)

用五个优先队列分别维护即可。

第二个做法是注意到下标 \(\mod 2\) 是凸的,因此考虑 \(i\to i+2\)

CF335F

这题的题解都在写什么鬼

从高往低做
假设现在买了 \(x\),附赠了 \(y\),现在在处理值为 \(z\) 的,\(y\) 是附赠品里最便宜的。

  1. 如果买下 \(y\),就可以送两个 \(z\),代价是 \(y\)

  2. 否则直接买下两个 \(z\),赠送两次白嫖机会
    那么就有

  3. \(2z>y\) 时,买 \(y\) 来换会更优秀,这相当于是 \(y\) 和买 \(y\) 的那个元素同时提供了两次白嫖机会

  4. 与此同时,需要注意到,如果直接买两个 \(z\),也能会后续提供两次白嫖机会,因此等效为额外买了一个 \(2z-y\) 的物品

讨论一下:

  1. 只有一个 \(z\)

    1. \(y\ge z\) 肯定只有直接买了。\(y\) 仍然是白嫖物品。
    2. \(y<z\) 买下 \(y\),还能给更小的值白嫖一个,这时候去掉 \(y\),新的白嫖物品是 \(z\)

    综上,代价是 \(\min(z,y)\)

  2. 有不止一个 \(z\)

    1. \(y<z\)

      肯定买下 \(y\),白嫖两个 \(z\),代价 \(y\)

      那么白嫖物品就除掉 \(y\),加入两个 \(z\)

      \(y\) 在此刻消耗掉是最优的决策(白嫖了最多的 \(2z\))。

    2. \(y< 2z\)

      买下 \(y\) 显然更划算,这时候白嫖物品是两个 \(z\)

      直观的想法是我们加入两个 \(z\) 作为新的白嫖物品,但这是错误的。

      注意到,我们每次消耗白嫖物品实质上是要多出两个白嫖额度,这一步操作以后我们要多出两个白嫖额度,就只剩下几个操作:

      1. 买下一个 \(z\)

      2. 买下两个 \(z\),提供两个白嫖额度,\(y\) 就不用买了,代价是 \(2z-y\)

      注意到 \(z\le y<2z\implies 2z-y\le z\),也就是一定会先买两个 \(z\) 而撤掉买 \(y\) 的决策(后续买也不迟),也即买一个 \(z\) 这种事情不可能发生。

      因此,我们实际上为未来有两个决策,先执行 \(2z-y\),再执行 \(y\)

      所以这相当于是两个新的白嫖物品。

    3. \(y\ge 2z\)

      显然应当直接买,不消耗\(y\)

因此可以用优先队列维护这个贪心过程。

P8175

假定我们已经知道了我们需要买哪些糖果,如何走才能够保证拿完?

一个很自然的观察是我们从左往右拿糖果去消耗,因为从右往左只会让左边的限制更为严格。

首先,在同一位置的糖果,在取最后一个之前,一定是取了之后去最近的空地消耗。

不妨设 \(d_i\) 为商店 \(i\) 距离其最近的空地距离。

取最后一个之后,有两个决策:

  1. 还是去最近的空地再返回
  2. 去下一个商店,路上如果有空地就直接消耗了。
  3. 去下一个商店的右侧的空地(中间没有空地),再回到下一个商店去取。

不妨设 \(pre,suf\) 为商店 \(i\) 左右最近空地距离,\(nxt\) 为下一个商店。三个策略的代价分别是:

\[\begin{cases} 2\min(i-pre_i,suf_i-i)+nxt_i-i\\ nxt_i-i,suf_i<nxt_i\\ suf_i-i+suf_i-nxt_i\\ \end{cases} \]

不妨记这个值是 \(f_i\),那么我们其实就已经明白代价了。

如果提前去掉走路的代价 \(nxt_i-i\)(因为这段路是必须要走的)。

问题就转化为了,我们从左往右取商品,在商店 \(i\) 的代价是这样的:

  1. 取首个商品的代价为 \(f_i\),保证 \(f_i\le d_i\)
  2. 后续每个商品 \(d_i\) 块。

我们需要满足,对于每个 \(i\),除掉最后一个购买的商品的话(你可以拿在手上跑去卖掉,只要抢先买下就行)在所有商店 \(j\le i\) 买的商品总值不超过 \(i-1\)。注意这里的最后一个买指的是 \(f_i\)

这可以使用反悔贪心解决,我们维护一个堆,里面放我们所有的买下的糖果的代价。

在走到商店 \(i\) 时:

  1. 首先尽可能买 \(d_i\),直到首次超限。

    超限也可以考虑把之前更贵的删了换成我。

    因为有 \(f_i\le d_i\),所以对于一个商店的撤销,一定先撤普通商品再撤这个特殊商品。

  2. 然后尽可能买一个 \(f_i\),策略和上面一样,如果买不了就说明上面也一个没买。

考虑分析一下复杂度。

由于 \(\sum d\)\(O(n)\) 级的,因此 \(d,f\) 合起来只有 \(O(\sqrt n)\) 种取值。

而一个值最多被插入和撤销优先队列 \(O(\frac{n}{i})\) 次,并且只能被更小的值替换。

因此每个值至多在优先队列里变动(增删) \(O(\frac{n}{i})\) 次。

所以优先队列的插入删除不超过 \(O(\sum \frac{n}{i})=O(n\ln n)\) 次,因此复杂度不超过 \(O(n\log n\ln n)\)

ABC363G

这太板了。

首先考虑原问题如何解决,这是一个经典贪心问题:

将所有任务按照截止时间 \(D_i\) 排序,用优先队列维护当前所选的任务。

从头开始扫描:

  1. 如果没到截止时间,直接将 \(P_i\) 加入优先队列。
  2. 否则观察优先队列里收益最低的是否比我低,如果比我低就拿我去替换。

让我们观察一下,这是在做什么,如何能够支持单修。

首先,由于需要排序,我们定然是要按照 \(D\) 为轴建立数据结构的。

然后考虑一个任务的影响。

假定我现在已经决定了当前最优的选择,现在加入了一个任务。

  1. 首先假装我选中这个任务。
  2. 如果现在合法,那自然最优。
  3. 否则找到最劣的任务,使得删掉它可以重新合法。

删除是同理的:

  1. 如果删的不在最优决策了,不管。
  2. 否则,观察有哪些任务可以加入决策集合,找最优的加入。

显然,决策集合的变动最多是 \(O(1)\) 的。

那么如何判断合法性?根据Hall定理:

  1. 对于任意时间 \(D\),截止时间 \(\le D\) 的被执行任务个数 \(\le D\)

那么线段树初始化节点 \(i\) 权值为 \(i\),每次执行一个任务,就将 \([D_i,n]\) 减一。

只要全局最小值非负就合法。

那么上述就可以通过模拟实现了,只需要维护区间内尚未加入决策集合的最大权值,加入决策集合的最小权值,支持查找最左侧 \(-1\),最右侧 \(0\),然后支持区间加减即可。

posted @ 2026-01-27 15:13  spdarkle  阅读(11)  评论(0)    收藏  举报