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的序列ai,给出一个整数c定义序列中一段长度为k的区间的贡献为区间和减去前⌊k/c⌋小数的和,现在要给序列ai做一个划分,使得贡献的总和最小。
解题思路:首先可以证明出最优方案中一定存在所有划分的块的元素量都不大于c。可以考虑dp,dp[i]表示前i个元素可以被砍掉的最大和,则只需考虑第i个元素是否被取到即可,被取到时,dp[i] = dp[i - c] + min(a[i - c + 1...i]);没被取到时,dp[i] = dp[i - 1]。其中求最小值可以用线段树维护。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <vector>
#include <bitset>
#include <functional>
using namespace std;
#define LL long long
const int INF = 0x3f3f3f3f;
int mi[400015], n, c;
LL sum;
LL dp[100015];
void build(int k, int l, int r)
{
if (l == r)
{
scanf("%d", &mi[k]);
sum += mi[k];
return;
}
int mid = l + r >> 1;
build(k << 1, l, mid);
build(k << 1 | 1, mid + 1, r);
mi[k] = min(mi[k << 1], mi[k << 1 | 1]);
}
int query(int k, int l, int r, int ll, int rr)
{
if (ll <= l && r <= rr) return mi[k];
int mid = l + r >> 1;
int ans = INF;
if (ll <= mid) ans = min(ans, query(k << 1, l, mid, ll, rr));
if (mid < rr) ans = min(ans, query(k << 1 | 1, mid + 1, r, ll, rr));
return ans;
}
int main()
{
while (~scanf("%d%d", &n, &c))
{
sum = 0;
build(1, 1, n);
memset(dp, 0, sizeof dp);
for (int i = c; i <= n; i++)
{
int tmp = query(1, 1, n, i - c + 1, i);
dp[i] = max(dp[i - 1], dp[i - c] + tmp);
}
printf("%lld", sum - dp[n]);
}
return 0;
}