CFGYM104976C. Bookface 详细题解
38th Petrozavodsk Programming Camp, Winter 2020 Day 5: Jagiellonian U Contest, Sunday, February 2, 2020
C. Bookface
将题意转化后可以变成这样一个问题,转化完之前的题面在文章后面。
给一个长为 \(n\) 的数列 \({a}\),每次花费 \(1\) 的代价对 \(a\) 中的任意一个元素加 \(1\) 或减 \(1\),不限制次数,问最少花费多少可以使这个数列不降。
由于是根据标程写原理,所以下面很多概念的提出完全没有根据和思维过程。(所以先熟悉一下代码)
priority_queue<ll>q;ll ans=0;
for(int i=1;i<=n;++i){
if(!q.empty()&&q.top()>a[i]){
ans+=q.top()-a[i];
q.pop();
q.push(a[i]);
}
q.push(a[i]);
}
这个问题可以转化为将一个数列分成若干段,每段内的数变成这段数的中位数,然后这些中位数从左往右递增。(至于为什么是中位数然后为什么可以这样转化,感觉比较显然)
现在我们假设已经划分好了若干段,设段数为 \(m\),然后这些分段也是当前最优的。再设当前段的中位数是 \(x_m\),上一段的中位数为 \(x_{m-1}\),以此类推。现在加进来一个数 \(a_i\)。
如果 \(a_i>x_m\),直接新建一段,\(a_i\) 作为这新的一段目前唯一的元素。如果 \(a_i=x_m\),这个数直接加进这一段也不影响什么。
如果 \(a_i<x_m\),当前段的中位数可能因此改变(当然也可能不改),如果当前段的中位数因此变小了,那么可能 \(x_{m-1}\geq x_m\) 甚至 \(x_j\geq x_m(j<m-1)\),就与我们要求的每段中位数递增就不符了。我们可能要合并一些段,假设新的一段为中位数为 \(x'\),必然有 \(x'<x_m\),那么原来这段里 \(\geq x_m\) 的数肯定要变小变成 \(x'\),而 \(<x_m\) 的部分数可能要变小、可能不变,可能变大。
我们先不考虑合并什么的,假设目前就一段,而且保证每次加进来的数都比当前段的中位数小,怎么求加入后这一段所有数变成新中位数的花费,(当然我们已知原来一段变成原来段中位数的花费)。
还是假设中位数 \(x_m\)。可以发现对于原数列中每个数满足 \(y>x_m\),都能找到 \(z<x_m\) 与它们一一对应(中位数的性质),对于这样的一对 \(y,z\),它们对答案的贡献就是 \(y-z\)。
假设加入数 \(a_i\) 后新中位数 \(x_m'\in[z,x_m]\),那么 \(y,z\) 对答案的贡献不改变。事实上这个中位数最小只能到 \(z\),因为 \(z\) 存在于这个数列中。也就是说答案的贡献只需要加上 \(x_m-a_i\) 即可。
现在还存在一个问题,那就是假如将来中位数减到 \(z\) 以下的一个数 \(x_m''\),那么 \(y,z\) 对答案的贡献势必要加上 \(2\times (z-x_m'')\)。由于我们改变中位数的时候可以让 \(z\) 与后新加进来的数 \(w_1<x_m''\) 重新配对,并加上贡献 \((z-w_1)\) 同时使 \(z\) 和 \(w_1\) 对答案的贡献满足要求,只需要考虑 \(y\) 的问题。
我们开一个大根堆维护这一段中所有的元素。一个天才的想法就是将 \(y\) 与 \(z\) 配对后将 \(y\) 弹出堆顶然后将 \(y\) 变成 \(z\) 再次加入堆,这样未来会找到一个新加进来的数 \(w_2<x_m''\) 与 \(y\) 配对,答案的贡献加上 \((z-w_2)\) 同时使 \(y\) 和 \(w_2\) 对答案的贡献满足要求。于此同时可以发现进行这样的操作之后堆顶的元素就是原数列的中位数。
实际上这个时候再考虑多段之间合并的问题,就发现这个问题已经被解决了。因为当前段的中位数减到比上一段的中位数小的时候,上一段的中位数就会自然地纳入我们新加入的数的考量范畴,同一份代码已经完成了合并。
BSE经典写一些很啰嗦的废话,将就着看吧。
原题面及初步转化
给一个长为 \(n\) 的序列 \(c\),每次花费 \(1\) 的代价对 \(c\) 中的任意一个元素加 \(1\) 或减 \(1\),不限制次数,但是不能减到小于 \(0\),问最少花费多少可以使这个数列中任意两个元素的绝对值之差大于 \(d\)。
显然排序之后让相邻两个元素之差大于等于 \(d\) 是最优的。
如果我们将 \(-d,-2d,-3d,\dots,-(n+1)d\) 加入这个序列中,那么我们将一个数减到小于 \(0\) 就要多花费 \((n+1)\) 的代价,是不如同时将原数列中的数都 \(+1\) 的,这样我们就巧妙地解决了这个不能减到 \(0\) 的问题。
然后我们再让第 \(i\) 个元素减上 \(i\times d\),这样对于原数列中之差要大于 \(d\) 的相邻元素只需要满足新数列中数列不降即可。
本文作者:BigSmall_En
本文链接:https://www.cnblogs.com/BigSmall-En/p/18461605
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步