[BZOJ3156]防御准备 斜率优化DP

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3156

裸的斜率优化,记一下以后复习用吧。

要直接dp很明显应该要倒着dp,很不爽,先把它倒过来。

令$sum[j]=\sum_{i=1}^ji$,于是我们首先推出这样一个方程$$f[i]=min\{f[j]+sum[i-1]-sum[j]-(i-j-1)*j+a[i]\}$$

这样dp复杂度是$O(n^{2})$的,考虑斜率优化。

设$k>j$,且$k$优于$j$,则有$$f[k]+sum[i-1]-sum[k]-(i-k-1)*k+a[i]<f[j]+sum[i-1]-sum[j]-(i-j-1)*j+a[i]$$

整理得$$f[k]-f[j]-(sum[k]-sum[j])+k^{2}-j^{2}+k-j<i*(k-j)$$

把$k-j$直接除过去,得$slope(k,j)<i$,因为$i$是单调递增的,所以只要有一次$k$优于$j$,$k$永远都优于$j$,直接把$j$给T掉就好了。

这样就可以用单调队列来维护。

 

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 typedef long long ll;
 6 const ll INF=1LL<<62;
 7 int inline readint(){
 8     int Num;char ch;
 9     while((ch=getchar())<'0'||ch>'9');Num=ch-'0';
10     while((ch=getchar())>='0'&&ch<='9') Num=Num*10+ch-'0';
11     return Num;
12 }
13 int n,data[1000010],a[1000010];
14 ll f[1000010],sum[1000010];
15 int q[1000010],h,t;
16 double slope(int k,int j){
17     return (double)(f[k]-f[j]-(sum[k]-sum[j])+(ll)k*k-(ll)j*j+k-j)/(k-j);
18 }
19 int main(){
20     n=readint();
21     for(int i=1;i<=n;i++) data[i]=readint();
22     for(int i=1;i<=n;i++){
23         a[i]=data[n-i+1];
24         sum[i]=sum[i-1]+i;
25     }
26     h=t=1;
27     f[1]=a[1];
28     q[1]=1;
29     ll ans=f[1]+sum[n]-sum[1]-n+1;
30     for(int i=2;i<=n;i++){
31         while(h<t&&slope(q[h+1],q[h])<=i) h++;
32         f[i]=f[q[h]]+sum[i-1]-sum[q[h]]-(ll)(i-q[h]-1)*q[h]+a[i];
33         ans=min(ans,f[i]+sum[n]-sum[i]-(ll)(n-i)*i);    
34         while(h<t&&slope(q[t],q[t-1])>=slope(i,q[t])) t--;
35         q[++t]=i;
36     }
37     printf("%lld",ans);
38     return 0;
39 }

 

转载于:https://www.cnblogs.com/halfrot/p/7440098.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值