题目大意:
给出一个有N (1<=N<=400000)个正数的序列,要求把序列分成若干组(可以打乱顺序),每组的元素个数不能小于T (1 < T <= N)。每一组的代价是每个元素与最小元素的差之和,总代价是每个组的代价之和,求总代价的最小值。
承接上一篇再刷一道题,唯一不同就是有一个区间限定。
不过多阐述了,直接贴代码。要看斜率优化分析,请转至
斜率优化
/*
首先 sort 从小到大排序。
然后 dp[i] 前 i 个数分隔得到的最小值。
则 dp[i]= min{dp[j]+(sum[i]-sum[j])-a[j+1](i-j)} k<=j<i-t+1.
之后就是斜率优化了。
*/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define LL long long
using namespace std;
const int MAXN = 400010;
int que[MAXN];
LL sum[MAXN], a[MAXN], dp[MAXN];
LL getDP(int i, int j){
return dp[j] + (sum[i] - sum[j]) - a[j + 1] * (i - j);
}
LL getUP(int j, int k){
return dp[j] - sum[j] + j * a[j + 1] - dp[k] + sum[k] - k * a[k + 1];
}
LL getDOWN(int j, int k){
return a[j + 1] - a[k + 1];
}
/*
定义状态dp[i]表示排序后前i个数分组后的最小代价。
dp[i] = dp[j] + (sum[i] –sum[j]) –a[j+1] * (i-j)
dp[i] = dp[k] + (sum[i] –sum[k]) –a[k+1] * (i-k)
斜率优化维护一个凸包,使队首的第一个线段的斜率最优
需要 dp[j] + (sum[i] –sum[j]) – a[j+1] * (i-j) > dp[k] + (sum[i] –sum[k]) - a[k+1] * (i-k)
所以 dp[j] + sum[i] –sum[j] –a[j+1] * i + a[j+1] * j > dp[k] + sum[i] - sum[k] - a[k+1] * i + a[k+1] * k)
所以 dp[j] –sum[j] –a[j+1] * i + a[j+1] * j > dp[k] - sum[k] - a[k+1] * i + a[k+1] * k)
所以 dp[j] - dp[k] - sum[j] + sum[k] + a[j+1] * j - a[k+1] * k > i * (a[j+1] - a[k+1])
UP > i * DOWN
*/
int main(){
int n, t;
int head, tail;
while(scanf("%d%d", &n, &t) == 2){
dp[0] = sum[0] = a[0] = 0;
for(int i=1;i<=n;i++){
scanf("%I64d", &a[i]);
}
sort(a+1, a+n+1);
for(int i=1; i<=n; i++)
sum[i] = sum[i-1] + a[i];
head = tail = 0;
que[tail++] = 0;
for(int i=1; i<=n; i++){
while(head + 1 < tail
&& getUP(que[head+1], que[head])
<= i * getDOWN(que[head+1], que[head])) head++;
dp[i] = getDP(i, que[head]);
int j = i - t + 1;//不能超过
if(j < t) continue;
while(head + 1 < tail
&& getUP(j, que[tail-1]) * getDOWN(que[tail-1], que[tail-2])
<= getUP(que[tail-1], que[tail-2]) * getDOWN(j, que[tail-1])) tail--;
/*原型是
getUP(j, que[tail-1]) / getDOWN(j, que[tail-1])
<= getUP(que[tail-1], que[tail-2]) / getDOWN(que[tail-1], que[tail-2])
但是为了防止炸精度,用交叉相乘
*/
que[tail++] = j;
}
printf("%I64d\n", dp[n]);
}
return 0;
}