一 原题
Since you are the best Wraith King, Nizhniy Magazin «Mir» at the centre of Vinnytsia is offering you a discount.
You are given an array a of length n and an integer c.
The value of some array b of length k is the sum of its elements except for the smallest. For example, the value of the array [3, 1, 6, 5, 2] with c = 2 is 3 + 6 + 5 = 14.
Among all possible partitions of a into contiguous subarrays output the smallest possible sum of the values of these subarrays.
The first line contains integers n and c (1 ≤ n, c ≤ 100 000).
The second line contains n integers ai (1 ≤ ai ≤ 109) — elements of a.
Output a single integer — the smallest possible sum of values of these subarrays of some partition of a.
3 5 1 2 3
6
12 10 1 1 10 10 10 10 10 10 9 10 10 10
92
7 2 2 3 6 4 5 7 1
17
8 4 1 3 4 5 5 3 4 1
23
In the first example any partition yields 6 as the sum.
In the second example one of the optimal partitions is [1, 1], [10, 10, 10, 10, 10, 10, 9, 10, 10, 10] with the values 2 and 90 respectively.
In the third example one of the optimal partitions is [2, 3], [6, 4, 5, 7], [1] with the values 3, 13 and 1 respectively.
In the fourth example one of the optimal partitions is [1], [3, 4, 5, 5, 3, 4], [1] with the values 1, 21 and 1 respectively.
二 分析
题意:给一个长度为n的数组,你可以把数组切成若干份。对于每一份,如果长度为k,删除其中最小的k/c下取整个数(c给定),把剩下的数求和。最后把若干份的和再加在一起,问结果最小是多少。
分析:满足最优子结构和无后效性,很容易想到DP。琢磨一下,发现一个可行的策略是每份子数组长度只取1或c。这样我们状态转移的代价可以降到O(n),但事先需要储存一下所有长度为c的子数组的最小值以及和,用multiset处理的话代价是O(nlgn)。
三 代码
/*
AUTHOR: maxkibble
PROB: cf 940E
LANG: c++ 17
*/
#include <cstdio>
#include <set>
typedef long long LL;
const int maxn = 1e5 + 5;
int n, c, a[maxn], mina[maxn];
LL sum[maxn], dp[maxn];
std::multiset<int> seg;
int main() {
scanf("%d%d", &n, &c);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
seg.insert(a[i]);
sum[i] = sum[i - 1] + a[i];
if (seg.size() > c) {
seg.erase(seg.lower_bound(a[i - c]));
sum[i] -= a[i - c];
}
mina[i] = *seg.begin();
}
for (int i = 1; i <= n; i++) {
if (i < c) { dp[i] = dp[i - 1] + a[i]; continue; }
dp[i] = std::min(dp[i - 1] + a[i], dp[i - c] + sum[i] - mina[i]);
}
printf("%lld\n", dp[n]);
return 0;
}