Picnic Cows HDU - 3045
给一个序列
{
a
[
i
]
,
i
∈
[
1
,
n
]
}
\{a[i],i\in[1,n]\}
{a[i],i∈[1,n]} ,对这些数进行分组,每组至少
k
k
k 个,每组内每个数都会减少到组中最小的那个数,求整体最小能减少多少?
首先肯定要排序,从小到大排序。
令 s [ i ] s[i] s[i] 为前缀和, d p [ i ] dp[i] dp[i] 为前 i i i 个数减少的最小值,状态转移为:
d p [ i ] = min { d p [ j ] + s [ i ] − s [ j ] − a [ j + 1 ] × ( i − j ) } dp[i]=\min\{dp[j]+s[i]-s[j]-a[j+1]\times(i-j)\} dp[i]=min{dp[j]+s[i]−s[j]−a[j+1]×(i−j)}
开始推斜率DP:令 k < j k<j k<j, j j j 比 k k k 优,则:
d p [ j ] + s [ i ] − s [ j ] − ( i − j ) a [ j + 1 ] < d p [ k ] + s [ i ] − s [ k ] − ( i − k ) a [ k + 1 ] d p [ j ] − s [ j ] + j a [ j + 1 ] − ( d p [ k ] − s [ k ] + k a [ k + 1 ] ) < i a [ j + 1 ] − i a [ k + 1 ] ( d p [ j ] − s [ j ] + j a [ j + 1 ] ) − ( d p [ k ] − s [ k ] + k a [ k + 1 ] ) a [ j + 1 ] − a [ k + 1 ] < i \begin{aligned} dp[j]+s[i]-s[j]-(i-j)a[j+1]&<dp[k]+s[i]-s[k]-(i-k)a[k+1]\\ dp[j]-s[j]+ja[j+1]-(dp[k]-s[k]+ka[k+1])&<ia[j+1]-ia[k+1]\\ \frac{(dp[j]-s[j]+ja[j+1])-(dp[k]-s[k]+ka[k+1])}{a[j+1]-a[k+1]}&<i \end{aligned} dp[j]+s[i]−s[j]−(i−j)a[j+1]dp[j]−s[j]+ja[j+1]−(dp[k]−s[k]+ka[k+1])a[j+1]−a[k+1](dp[j]−s[j]+ja[j+1])−(dp[k]−s[k]+ka[k+1])<dp[k]+s[i]−s[k]−(i−k)a[k+1]<ia[j+1]−ia[k+1]<i
得
y
j
=
d
p
[
j
]
−
s
[
j
]
+
j
a
[
j
+
1
]
,
x
j
=
a
[
j
+
1
]
y_j=dp[j]-s[j]+ja[j+1],x_j=a[j+1]
yj=dp[j]−s[j]+ja[j+1],xj=a[j+1],转移的时候再注意
i
,
j
i,j
i,j 的范围。
代码如下:
#include<iostream>
#include<cstring>
#include<algorithm>
//#define WINE
#define MAXN 400010
using namespace std;
typedef long long ll;
int T,n,h,t,q[MAXN];
ll a[MAXN],s[MAXN],dp[MAXN];
ll up(int j,int k){
return dp[j]-s[j]+j*a[j+1]-(dp[k]-s[k]+k*a[k+1]);
}
ll down(int j,int k){
return a[j+1]-a[k+1];
}
ll getDP(int i,int k){
return dp[k]+s[i]-s[k]-a[k+1]*(i-k);
}
int main(){
#ifdef WINE
freopen("data.in","r",stdin);
#endif
while(scanf("%d%d",&n,&T)!=EOF){
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
sort(a+1,a+n+1);
for(int i=1;i<=n;i++)s[i]=s[i-1]+a[i];
memset(dp,0,sizeof(dp));
h=t=0;q[t++]=0;dp[T]=s[T]-s[1]*T;
for(int i=T+1;i<=n;i++){
while(h+1<t&&up(q[h+1],q[h])<i*down(q[h+1],q[h]))h++;
dp[i]=getDP(i,q[h]);
int j=i-T+1;
if(j<T)continue;
while(h+1<t&&up(j,q[t-1])*down(q[t-1],q[t-2])<=up(q[t-1],q[t-2])*down(j,q[t-1]))
t--;
q[t++]=j;
}
printf("%lld\n",dp[n]);
}
return 0;
}