hdu3045 斜率优化

It’s summer vocation now. After tedious milking, cows are tired and wish to take a holiday. So Farmer Carolina considers having a picnic beside the river. But there is a problem, not all the cows consider it’s a good idea! Some cows like to swim in West Lake, some prefer to have a dinner in Shangri-la ,and others want to do something different. But in order to manage expediently, Carolina coerces all cows to have a picnic!
Farmer Carolina takes her N (1

/*
首先 sort 从小到大排序。
然后 dp[i] 前 i 个数分隔得到的最小值。
则  dp[i]= min{dp[j]+(sum[i]-sum[j])-a[j+1](i-j)}    k<=j<i-t+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
*/
#include<stdio.h>
#include<algorithm>
using namespace std;
typedef long long dnt;
const int maxn=400001;
int n,T,h,t;
int q[maxn];
dnt dp[maxn],sum[maxn],a[maxn];
inline const dnt read(){
    register dnt f=1,x=0;
    register char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
inline dnt dd(int i,int j){
    return dp[j]+sum[i]-sum[j]-a[j+1]*(i-j); 
}
inline dnt up(int j,int k){
    return dp[j]-sum[j]+a[j+1]*j-dp[k]+sum[k]-a[k+1]*k;
}
inline dnt down(int j,int k){
    return a[j+1]-a[k+1];
}
int main()
{
    while(scanf("%d%d",&n,&T)!=EOF){
      dp[0]=sum[0]=a[0]= 0;
      for(register int i=1;i<=n;i++) a[i]=read();
      sort(a+1,a+n+1);
      for(register int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];
      for(register int i=T;i<=min(2*T-1,n);i++)  dp[i]=sum[i]-i*a[1];  
      h=1,t=1;
      q[t]=0;
      for(register int i=2*T-1;i<=n;i++){
          while(h<t&&up(q[h+1],q[h])<=i*down(q[h+1],q[h])) h++;
          dp[i]=dd(i,q[h]);
          int j=i-T+1;
          while(h<t&&up(j,q[t])*down(q[t],q[t-1])<=up(q[t],q[t-1])*down(j,q[t])) t--;
          q[++t]=j;
      }
      printf("%I64d\n",dp[n]);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值