题意 :n个数,分成若干块,每一块的数量不能小于k,每一块的花费是所有的数减去块中最小值的和。每一块和的最小值。
显然要排序以后分块,设
f i
表示(1,i)分块后的最小和,那么有
f i = m i n { f j + s u m i − s u m j − a j + 1 ∗ ( i − j ) ∥ ∥ i − j ≥ k }
假设
j
比k 更优
( k ≤ j )
f j + s u m i − s u m j − a j + 1 × ( i − j ) ≤ f k + s u m i − s u m k − a k + 1 × ( i − k )
整理一下就是斜率式子了
f j + j × a j + 1 − s u m j − f k − k × a k + 1 + s u m k a j + 1 − a k + 1 ≤ i
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std ;
#define maxn 400005
long long dp[maxn];
int n, k, que[maxn];
long long a[maxn], sum[maxn];
long long scan () {
char ch=' ' ;
while (ch<'0' ||ch>'9' )ch=getchar();
long long x=0 ;
while (ch<='9' &&ch>='0' )x=x*10 +ch-'0' ,ch=getchar();
return x;
}
long long up (int i, int j) {
return dp[i]+1L L*i*a[i+1 ]-sum[i] - (dp[j]+1L L*j*a[j+1 ]-sum[j]);
}
long long down (int i, int j) {
return a[i+1 ]-a[j+1 ];
}
int main () {
while (scanf ("%d%d" , &n, &k) == 2 ) {
sum[0 ] = 0 ;
for (int i = 1 ; i <= n; i++) a[i] = scan ();;
sort (a+1 , a+1 +n);
for (int i = 1 ; i <= n; i++) sum[i] = sum[i-1 ]+a[i];
dp[0 ] = 0 ;
for (int i = 1 ; i < k*2 && i <= n; i++) {
dp[i] = sum[i] - 1L L*i*a[1 ];
}
int L = 0 , R = 0 ;
que[R++] = 0 ;
que[R++] = k;
for (int i = 2 *k; i <= n; i++) {
while (L+1 < R && up (que[L+1 ], que[L]) <= 1L L*i*down (que[L+1 ], que[L]))
L++;
int j = que[L];
dp[i] = dp[j] + sum[i]-sum[j]-a[j+1 ]*(i-j);
while (L+1 < R && up (i-k+1 , que[R-1 ])*down (que[R-1 ], que[R-2 ]) <=
up (que[R-1 ], que[R-2 ])*down (i-k+1 , que[R-1 ]))
R--;
que[R++] = i-k+1 ;
}
printf ("%lld\n" , dp[n]);
}
return 0 ;
}