这是我斜率DP第一个没有一遍AC的,原因是第一遍忘开long long了。
这一题比较特殊,细心的同学一定发现了,递推式不带f。
为了方便,设d数组的后缀和为sd[i]=sd[i+1]+d[i],设k数组的前缀和为sk[i]=sk[i-1]+k[i](k[i]即是题目中的w[i])
设f[i]为第二个锯木厂选在i时的最小值,假设第一个锯木厂在j,从1~j-1运到j的和是k[p]*(sd[p]-sd[j]),p∈[1,n],从j+1~i-1运到i的和是k[p]*(sd[p]-sd[i]),p∈[j+1,i]。
从i+1~n运到第三个锯木厂的和是k[p]*sd[p],p∈[i+1,n]。设k[p]*sd[p]的和为sum。
那么整理一下此式为:f[i]=sum-sd[j]*sk[j]-sd[i]*sd[k]+sd[i]*sk[j],惊奇的发现不需要递推,可惜没有什么用。
整理成一次函数式:sd[j]*sk[j]=sd[i]*sk[j]+sum-f[i]-sd[i]*sk[i]
要让截距最大,且斜率sd[i]单调递减,那么考虑维护上凸包(不明白的一定要自己画图尝试!!)
看代码:
#include<bits/stdc++.h> using namespace std; #define int long long const int maxn=50000; int n,d[maxn],sd[maxn],sum,sk[maxn],k[maxn]; int q[maxn],f[maxn]; int yval(int a,int b){return sd[b]*sk[b]-sd[a]*sk[a];} int xval(int a,int b){return sk[b]-sk[a];} signed main(){ cin>>n; for(int i=1;i<=n;i++){ scanf("%lld%lld",&k[i],&d[i]); sk[i]=sk[i-1]+k[i]; } for(int i=n;i>=1;i--) sd[i]=sd[i+1]+d[i]; for(int i=1;i<=n;i++) sum+=k[i]*sd[i]; int l=1,r=1,ans=2147483647; for(int i=1;i<=n;i++){ while(l<r&&yval(q[l],q[l+1])>=xval(q[l],q[l+1])*sd[i])l++; f[i]=sd[i]*sk[q[l]]-sd[q[l]]*sk[q[l]]+sum-sd[i]*sk[i]; while(l<r&&yval(q[r-1],q[r])*xval(q[r],i)<=xval(q[r-1],q[r])*yval(q[r],i))r--; q[++r]=i; ans=min(ans,f[i]); } printf("%lld\n",ans); return 0; }