题解:AT_xmascon20_f Famous in Russia

· · 题解

前言

题解字数较多,但作者认为还是相对浅显易懂的。

第一部分:这个题到底在干啥?

正如副标题所言,我们拿到这题,似乎无从下手。在此之前,我们不妨考虑这样一个更加简单的问题:

问题 1:

给定 a_1\sim a_n,b_1\sim b_n,令 k=n,求做菜吃菜最短时间。

解析:将菜品按照 b_i 从大到小排序依次制作,答案为 \max_{i=1}^n(b_i+\sum_{j=1}^ia_j)

证明:

利用调整法。对于一个不是按照 b_i 从大到小排序的菜品序列,一定能找到下标 i1\le i<n),使得 b_i<b_{i+1}

\sum_{j=1}^{i-1}a_j=x,那么 i 处的答案为 ans_i=x+a_i+b_ii+1 处的答案为 ans_{i+1}=x+a_i+a_{i+1}+b_{i+1}

交换 i,i+1 菜品,此时对于 i+2\sim n 以及 1\sim i-1 处,答案不变,而 i,i+1 处的答案分别为 ans'_i=x+a_{i+1}+b_{i+1},ans'_{i+1}=x+a_i+a_{i+1}+b_i

  1. 由于 b_i<b_{i+1},因此 ans'_{i+1}<ans_{i+1}
  2. 由于 b_i<b_{i+1},因此 b_i<a_{i+1}+b_{i+1},因此 x+a_i+b_i<x+a_i+a_{i+1}+b_{i+1},因此 ans_i<ans_{i+1}
  3. 显然 x+a_{i+1}+b_{i+1}<x+a_i+a_{i+1}+b_{i+1},因此 ans'_i<ans_{i+1}
  4. 因此,\max(ans_i,ans_{i+1})>\max(ans'_i,ans'_{i+1}),交换显然更优。

对于上述问题,有没有一个更简单的刻画答案的方式?答案是肯定的。只需要将菜品按照 b_i 从小到大排序,维护答案 ans。遍历 i=1\sim n,令 ans:=\max(ans,b_i)+a_i。这本质只是倒过来计算答案。

问题 2:

给定 a_1\sim a_n,b_1\sim b_n,对于每个 k,求做菜吃菜最短时间。

解析:首先,将菜品按照 b_i 从小到大排序。忽略 a_i 被钦定为 0 的菜品,在最后将答案与 b_n 取较大值即可。接下来,研究以下 dp:

dp_{i,j}=\min(dp_{i-1,j},\max(dp_{i-1,j-1},b_i)+a_i)

可以发现,dp_{i,j} 表示在前 i 个菜品中挑选 j 个,仅它们的 a_j 不被钦定为 0,此时的答案最小值。那么对于每个 k,答案即为 ans_k=\max(dp_{n,k},b_n)

第二部分:有点头绪了,我们来优化这个 dp 吧!

一个全新的问题出现在我们面前:咋优化这个 dp?我们需要利用一些结论。

结论 1:

证明: 利用定义,可以发现在前 $i+1$ 个菜品中挑选 $j$ 个肯定不比在前 $i$ 个菜品中挑选劣,因为多了第 $i+1$ 个菜品供以选择。在前 $i$ 个菜品中挑选 $j$ 个肯定不比挑选 $j+1$ 个劣,因为多了一个需要选择的菜品。

根据结论 1 我们可以得知,对于每个 i,我们可以找到唯一一个 j_i1\le j_i\le i+1)使得对于所有 j<j_idp_{i,j}\le b_i

结论 2:

证明: 根据结论 1,对于 $j<j_i$,$dp_{i+1,j}\le dp_{i,j}\le b_i\le b_{i+1}$,因此 $j_{i+1}\ge j_i$。 **结论 3(重要):** 对于 $j<j_i$,$dp_{i,j}=dp_{i-1,j}$。 证明: 由于 $dp_{i,j}=\min(dp_{i-1,j},\max(dp_{i-1,j-1},b_i)+a_i)$,而 $\max(dp_{i-1,j-1},b_i)+a_i>b_i$,$dp_{i,j}\le b_i$,所以 $dp_{i,j}=dp_{i-1,j}$。 **结论 4(重要):** 对于 $j>j_i$,$dp_{i,j}=\min(dp_{i-1,j},dp_{i-1,j-1}+a_i)$。 证明: 由于 $dp_{i,j}=\min(dp_{i-1,j},\max(dp_{i-1,j-1},b_i)+a_i)$,而 $dp_{i-1,j-1}\ge dp_{i,j-1}>b_i$,所以 $dp_{i,j}=\min(dp_{i-1,j},dp_{i-1,j-1}+a_i)$。

铺天盖地的这么多结论,我们该如何利用它们呢?我们从差分数组考虑。

dif_{i,j}=dp_{i,j}-dp_{i,j-1}j_i\le j\le i)。

如上可得,对于 j>j_i+1dif_{i,j}\ge dif_{i,j-1}。而对于 j=j_idif 的复杂讨论,可以总结为以下两点:

所以,我们修改差分数组 dif 的定义。此时,记 dif_{i,j}=dp_{i,j}-dp_{i,j-1}j_i<j\le i),记 dif_{i,j_i}=dp_{i,j_i}-b_i

那么,dif_{i,j}\ge dif_{i,j-1}。差分数组 dif_{i,j_i}\sim dif_{i,i} 本质上只是:

这样,我们就有能力利用小根堆维护差分数组 dif

初始时堆中只有一个 a_1

第三部分:道理我都懂了,但是我们不知道 a_i,该咋办?

我们总结一下。

通过前两部分,我们得到了一个基于小根堆的一个算法,快速求解 dp 值贡献到答案中。但由于我们不知道 a_i 的值,这个算法似乎没什么实际用途。除非,我们把这个小根堆本身压入一个新的 dp,形成 dp 套 dp。

略微修改一下上述算法,将 id 变为全局变量便于操作,得到以下算法:

那我们的任务就是,将这个小根堆压入新的 dp 状态中。对于一个小根堆中的一个元素 x,我们需要知道:

g_{i,j,sum,x} 表示小根堆满足下标所示条件的 a_1\sim a_{i-1} 的组数。对于 a_i\in[1,v],我们尝试追踪 x 的去向:

据此,我们解决了几乎整道题目。上述 dp 几乎完美,只有一个问题:列出的转移式只是针对 a_1\sim a_{i-1} 这种以前已经被插入进来的元素进行实时追踪,插入的 a_i 该如何统计到 g 中并随着转移在以后追踪呢?

第四部分:解决新加入的 a_i 无法统计的问题

绞尽脑汁,我们似乎没有办法解决这个问题。我们的状态定义还是太简单了,似乎无法支持我们定位插入的 a_i 的位置。不妨更改状态,我们额外记一个信息:

根据插入 a_i 的规定,我们需要时刻记住,小根堆中的值是按大小为第一关键字,插入顺序为第二关键字排序的。这样,我们得知假如 a_i 被插入到 xnxt 之间,那么它一定满足 x\le a_i<nxt

对于每个 x,在 x 处定位所有满足 x\le a_i<nxta_i。具体地,记 f_{i,j,sum,x,nxt} 表示小根堆满足下标所示条件的 a_1\sim a_{i-1} 的组数。接下来定位 x 的去向以及追踪 a_i

到此为止,问题基本完成,剩下的便是一些小角箱子(corner case),例如小根堆中不存在元素该怎么办,nxt 不存在该怎么办,以及 a_i 若插入到小根堆堆头则上述转移无法统计到它等问题。这一部分留给读者自行推导,以加深理解。

总结

这个做法的时间复杂度为 O(n^3v^4),空间复杂度经滚动数组可以优化到 O(n^2v^3)