题意:将n个数分成若干组,每组数字的个数不少于t个,要把每组的数字减小到这组最小值,求所有数字减少的最小值。
先将这n个数从小到大排个序,可以想到一组里面的数一定是排序后相邻的。
设d(i)表示前i个数分完组以后减少的最小值,考虑j~i为一组,则有状态转移方程
还是一样的处理方法,设k < j ≤ i - t,且j~i为一组的值比k~i为一组的值更优。
则有不等式:
化简,把i分离出来,整理成斜率的形式:
写到这里就应该很清楚地能够看出来X和Y的表达式了。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 7 typedef long long LL; 8 9 const int maxn = 400000 + 10; 10 11 int n, t; 12 13 LL a[maxn], sum[maxn]; 14 LL d[maxn]; 15 16 int head, tail; 17 int Q[maxn]; 18 19 LL inline Y(int x) { return d[x-1] - sum[x-1] + a[x] * (x - 1); } 20 21 LL inline DY(int p, int q) { return Y(q) - Y(p); } 22 23 LL inline DX(int p, int q) { return a[q] - a[p]; } 24 25 int main() 26 { 27 while(scanf("%d%d", &n, &t) == 2) 28 { 29 for(int i = 1; i <= n; i++) scanf("%I64d", a + i); 30 sort(a + 1, a + 1 + n); 31 for(int i = 1; i <= n; i++) sum[i] = sum[i-1] + a[i]; 32 33 memset(d, 0, sizeof(d)); 34 for(int i = t; i < 2 * t && i <= n; i++) d[i] = sum[i] - a[1] * i; 35 36 head = tail = 0; 37 Q[tail++] = 1; 38 for(int i = t * 2; i <= n; i++) 39 { 40 while(head + 1 < tail && DY(Q[tail-1], i-t+1) * DX(Q[tail-2], Q[tail-1]) <= DY(Q[tail-2], Q[tail-1]) * DX(Q[tail-1], i-t+1)) tail--; 41 Q[tail++] = i - t + 1; 42 while(head + 1 < tail && DY(Q[head], Q[head+1]) <= DX(Q[head], Q[head+1]) * i) head++; 43 d[i] = d[Q[head]-1] + sum[i] - sum[Q[head]-1] - (i-Q[head]+1) * a[Q[head]]; 44 } 45 46 printf("%I64d\n", d[n]); 47 } 48 49 return 0; 50 }
代码君
时间: 2024-10-09 22:18:32