Codeforces Round 887 (Div. 2)

https://codeforces.com/contest/1853

E. Ina of the Mountain

题意

给出一个长度为 nn 的数组 aa,满足 1aik1\leq a_i\leq k。每次操作可以选定一个区间 [l,r][l,r],让其中所有的数减 11。当一个数变成 00 后,它会立刻变成 kk。求最少需要多少次操作,才能让所有的数都变成 kk

题解

一道反悔贪心的题目,感觉很巧妙。

我们先考虑一个经典问题:给出一个数组 aa,需要多少次同样的操作能将它变成全 00。答案是 i=1nmax(0,aiai1)\sum\limits_{i=1}^n\max(0,a_i-a_{i-1})

这道题目相当于是可以给一些 aia_i 加上 kk,求上述式子的最小值。我们考虑反悔贪心:

  • ai<ai1a_i<a_{i-1},那么该项的贡献为 00
  • ai>ai1a_i>a_{i-1},有两种决策
    • 贡献为 aiai1a_i-a_{i-1}
    • 选取某个 j<ij<i,给 aj,,ai1a_j,\cdots,a_{i-1} 都加上 kk,贡献为 ajaj1+ka_j-a_{j-1}+k

我们用一个优先队列来维护所有可能的决策(也就是反悔堆),若 ai<ai1a_i<a_{i-1},则将 aiai1+ka_i-a_{i-1}+k 放入队列,否则将 aiai1a_i-a_{i-1} 放入队列。这是因为我们选择反悔的位置 jj 一定满足 aj<aj1a_j<a_{j-1},不然 ajaj1+ka_j-a_{j-1}+k 大于 kk,得到的结果一定不会被选择。

补充说明:对于 ai1a_{i-1} 已经加上了 kk,再取 aiai1a_i-a_{i-1} 的决策,就相当于是给 aia_i 也加上了 kk

具体细节参考代码。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;cin>>t;
while(t--)
{
int n,k;cin>>n>>k;
priority_queue<int,vector<int>,greater<int>> q;
int lst=0;ll ans=0;
for(int i=1;i<=n;i++)
{
int x;cin>>x;x%=k;
if(x>lst)
{
q.push(x-lst);
ans+=q.top();
q.pop();
}
else q.push(k+x-lst);
lst=x;
}
cout<<ans<<'\n';
}
return 0;
}

Codeforces Round 887 (Div. 2)
https://je3ter.github.io/2023/08/09/ACM/Codeforces Round 887 (Div. 2)/
作者
Je3ter
发布于
2023年8月9日
许可协议