题意:
有n个奶牛分别有对应的兴趣值,现在对奶牛分组,每组成员不少于t,
在每组中所有的成员兴趣值要减少到该组的最小值,问总共最少需要减少的兴趣值是多少。
思路:
斜率优化+DP
转移方程为:f[i]=min{ f[j]+sum[i]-sum[j]+(i-j)*a[j+1] } T<=j<=i-T
其中sum表示a的前缀和。
如果j>k且决策j优于决策k则有
f[j]-f[k]+sum[k]-sum[j]-k*a[k+1]+j*a[j+1]<i*(a[j+1]-a[k+1])
维护指定区间内的下凸包即可。
需要注意的是输入输出用int64,而且斜率部分不能用之前直接除的写法了,因为a有long long,可能误差会比较大,改用化除为乘的方法。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mem(a) memset(a,0,sizeof(a))
#define mp(x,y) make_pair(x,y)
const int INF = 0x3f3f3f3f;
const ll INFll = 0x3f3f3f3f3f3f3f3fll;
inline ll read(){
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
//
const int maxn = 4e5+10;
int L,R,n,T,q[maxn];
ll a[maxn],dp[maxn],sum[maxn];
ll getup(int j,int k){
return dp[j]-dp[k]-sum[j]+sum[k]+j*a[j+1]-k*a[k+1];
}
ll getdown(int j,int k){
return a[j+1]-a[k+1];
}
int main(){
while(scanf("%d%d",&n,&T)==2){
dp[0]=sum[0]=0;a[0]=0;
for(int i=1; i<=n; i++)
scanf("%I64d",&a[i]);
sort(a+1,a+1+n);
for(int i=1; i<=n; i++)
sum[i] = sum[i-1]+a[i];
int L=0,R=0;
for(int i=1; i<=n; i++){
while(L<R && getup(q[L+1],q[L]) <= i * getdown(q[L+1],q[L])) L++;
int j = q[L];
dp[i] = dp[j]+(sum[i]-sum[j])-(i-j)*a[j+1];
int t = i-T+1;
// 从t~i是一组, T是最小的长度, 所以要考虑R与第t个比较,如果与t+1比较,那可能会
//把第t个弹出,就不可能是最优解了,也就是不能形成长度为>=T的按顺序的一段。
if(t >= T){
while(L<R && getup(t,q[R])*getdown(q[R],q[R-1]) <= getup(q[R],q[R-1])*getdown(t,q[R]))
R--;
q[++R] = t;
}
}
printf("%I64d\n",dp[n]);
}
return 0;
}