BZOJ3437 小P的牧场(斜率优化dp)
题目link:http://www.lydsy.com/JudgeOnline/problem.php?id=3437;
略略读一下题,发现这题是一道dp
有一些牧场:
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
其中编号大的可以管住编号小的.
a[i]表示建站费用 b[i]表示养殖奶牛数目
dp方程大概就出来了
f[i]=min(f[j]+cost(j+1,i)) (0<j<i);
可是有些问题需要O(1)计算cost(j+1,i);
怎么办呢?
于是我想了一个很不清真的计算方法
开了两个数组
形式化的讲:
sumb[i]=sumb[i-1]+b[i]
sum[i]=sum[i-1]+sumb[i-1]
有什么意义呢
sumb[i]表示b的前缀和
sum[i]表示如果1到i-1都要由i控制的运输费用;(不计建站费用)
第一个转移是显然的
第二个转移是因为可以将运输想象成两部分,先运到i-1,再运到i
然后就可以O(1)计算cost(j+1,i)了
cost(j+1,i)=sum[i]-sum[j]-sumb[j]*(i-j)
这个文字比较难解释,还是看图吧
矩形的长指i-k
矩形的宽指b[k]
(j<k<i)
则矩形的面积指该点对sum的贡献
很显然,现在cost=3号区域的面积
已知:总面积=sum[i]; 1号区域面积=sum[j]; 2号区域面积=sumb[j]*(i-j);
所以cost(j+1,i)=sum[i]-sum[j]-sumb[j]*(i-j);
/-----------------分割线-----------------/
现在f[i]=min(f[j]+sum[i]-sum[j]-sumb[j]*(i-j)+a[i])
f[i]=min(f[j]-sum[j]-sumb[j]*(i-j))+sum[i]+a[i];
开始斜率优化!
1.去掉min,确认是维护下凸包
2.f[i]=f[j]-sum[j]-sumb[j]*(i-j)+sum[i]+a[i];
3.化式子f[i]+i*sumb[j]=f[j]-sum[j]+sumb[j]*j+const(这一坨是跟j无关的常量)
4.对应 b +k x =y
然后一切就简单了
但是这题有坑点,a,b数组要开long long,题中的数据范围是假的
我因为括号写错了位置,WA一上午,最后只改了一个字符就AC了!QAQ
AC代码:
#include<bits/stdc++.h> using namespace std; const int N=1000010; int n,h,t,q[N]; long long a[N],b[N],sum[N],sumb[N],f[N]; inline double X(const int &j){ return sumb[j]; } inline double Y(const int &j){ return f[j]-sum[j]+sumb[j]*j; } inline double Rate(const int i,const int j){ return (Y(i)-Y(j))/(X(i)-X(j)); } int main(){ scanf("%d",&n); for (int i=1; i<=n; i++) scanf("%lld",&a[i]); for (int i=1; i<=n; i++) scanf("%lld",&b[i]); for (int i=1; i<=n; i++){ sumb[i]=sumb[i-1]+b[i]; sum[i]=sum[i-1]+sumb[i-1]; } for (int i=1; i<=n; i++){ while (h<t&&Rate(q[h],q[h+1])<i) ++h; f[i]=f[q[h]]+sum[i]-sum[q[h]]-sumb[q[h]]*(i-q[h])+a[i]; while(h<t&&Rate(q[t-1],q[t])>Rate(q[t],q[i])) t--; q[++t]=i; } printf("%lld",f[n]); }
不知道读者明白这题了没有,不明白的话请联系Yuhuger