此处只有由dp式子推导斜率优化
T[i]为T前缀和,C[i]为C前缀和
f[i]=min(f[j]+S*(C[n]-C[j])+T[i]*(C[i]-C[j])) (0<=j<i)
f[i]=min(f[j]-(S+T[i])*C[j])+S*C[n]+T[i]*C[i]
f[j]=(S+T[i])*C[j]+f[i]-S*C[n]-T[i]*C[i]
坐标(C[j],f[j])
#include <cstdio> #include <cstdlib> #include <algorithm> #define ll long long using namespace std; const int N=3e5+1; ll T[N],C[N],S,f[N]; int n,l,r,q[N]; int main() { scanf("%d%lld",&n,&S); for(int i=1;i<=n;i++) scanf("%lld%lld",&T[i],&C[i]),T[i]+=T[i-1],C[i]+=C[i-1]; l=1,r=0; for(int i=1;i<=n;i++) { while(l<r && (f[q[r]]-f[q[r-1]])*(C[i-1]-C[q[r]])>=(f[i-1]-f[q[r]])*(C[q[r]]-C[q[r-1]])) r--; q[++r]=i-1; while(l<r && f[q[l+1]]-f[q[l]]<=(S+T[i])*(C[q[l+1]]-C[q[l]])) l++; f[i]=f[q[l]]-(S+T[i])*C[q[l]]+S*C[n]+T[i]*C[i]; } printf("%lld\n",f[n]); return 0; }
- [SDOI2012]任务安排3
当斜率S+T[i]不再单挑递增,原本如同单调队列般的删除策略失效
但当斜率k为S+T[i]时,使凹壳f[i]最优点的本质是:
故使用二分查找
#include <cstdio> #include <cstdlib> #include <algorithm> #define ll long long using namespace std; const int N=3e5+1; ll T[N],C[N],S,f[N]; int n,l,r,q[N]; int mylower(ll x) { if(l==r) return q[l]; int L=l,R=r; while(L<R) { int mid=(L+R)>>1; if(f[q[mid+1]]-f[q[mid]]<=x*(C[q[mid+1]]-C[q[mid]])) L=mid+1; else R=mid; } return q[L]; } int main() { scanf("%d%lld",&n,&S); for(int i=1;i<=n;i++) scanf("%lld%lld",&T[i],&C[i]),T[i]+=T[i-1],C[i]+=C[i-1]; l=1,r=0; for(int i=1;i<=n;i++) { while(l<r && (f[q[r]]-f[q[r-1]])*(C[i-1]-C[q[r]])>=(f[i-1]-f[q[r]])*(C[q[r]]-C[q[r-1]])) r--; q[++r]=i-1; int j=mylower(S+T[i]); f[i]=f[j]-(S+T[i])*C[j]+S*C[n]+T[i]*C[i]; } printf("%lld\n",f[n]); return 0; }
A[i]=T[i]-(D[1]+D[2]+...+D[H[i]])
对A从小到大排序
S[i]=A[1]+A[2]+..+A[i]
f[i][j]=min(f[i-1][k]+A[j]*(j-k)-(S[j]-S[k])) (0<=k<j)
f[i][j]=min(f[i-1][k]-A[j]*k+S[k])+A[j]*j-S[j]
f[i-1][k]+S[k]=A[j]*k+f[i][j]+S[j]-A[i]*j;
坐标(k,f[i-1][k]+S[k])
#include <cstdio> #include <cstdlib> #include <algorithm> #define ll long long using namespace std; const int N=1e5+10,M=101; ll f[M][N],a[N],h[N],t[N],d[N],s[N],pre[N]; int n,m,p,l,r,q[N]; int main() { scanf("%d%d%d",&n,&m,&p); for(int i=2;i<=n;i++) scanf("%lld",&d[i]),d[i]+=d[i-1]; for(int i=1;i<=m;i++) scanf("%lld%lld",&h[i],&t[i]),a[i]=t[i]-d[h[i]]; sort(a+1,a+1+m); for(int i=1;i<=m;i++) s[i]=s[i-1]+a[i],f[1][i]=a[i]*i-s[i],pre[i]=f[1][i]+s[i]; for(int i=2;l=1,r=0,i<=p;i++) for(int j=1;j<=m;j++) { pre[j-1]=f[i-1][j-1]+s[j-1]; while(l<r && (pre[q[r]]-pre[q[r-1]])*(j-1-q[r])>=(pre[j-1]-pre[q[r]])*(q[r]-q[r-1])) r--; q[++r]=j-1; while(l<r && pre[q[l+1]]-pre[q[l]]<=a[j]*(q[l+1]-q[l])) l++; f[i][j]=f[i-1][q[l]]+a[j]*(j-q[l])-(s[j]-s[q[l]]); } printf("%lld\n",f[p][m]); return 0; }
S[i]=C[1]+C[2]+..+C[i]
f[i]=min(f[j]+(i-j-1+S[i]-S[j]-L)2) (0<=j<i)
化简: S[i]+=i,L++;
f[i]=min(f[j]+(S[i]-S[j]-L)2) (0<=j<i)
f[i]=min(f[j]+S[j]2-2*(S[i]-L)*S[j])+(S[i]-L)2
f[j]+S[j]2=2*(S[i]-L)*S[j]+f[i]-(S[i]-L)2
坐标(S[j],f[j]+S[j]2)
#include <cstdio> #include <cstdlib> #include <algorithm> #define ll long long using namespace std; const int N=5e4+10; int n,l=1,r,q[N]; ll L,c[N],s[N],pre[N],f[N]; ll pf(ll x) { return x*x; } int main() { scanf("%d%lld",&n,&L); L++; for(int i=1;i<=n;i++) { scanf("%lld",&c[i]); c[i]+=c[i-1]; s[i]=c[i]+i; } for(int i=1;i<=n;i++) { pre[i-1]=f[i-1]+pf(s[i-1]); while(l<r && (pre[q[r]]-pre[q[r-1]])*(s[i-1]-s[q[r]])>=(pre[i-1]-pre[q[r]])*(s[q[r]]-s[q[r-1]])) r--; q[++r]=i-1; while(l<r && pre[q[l+1]]-pre[q[l]]<=(s[q[l+1]]-s[q[l]])*2*(s[i]-L)) l++; f[i]=f[q[l]]+pf(s[i]-s[q[l]]-L); } printf("%lld\n",f[n]); return 0; }
SP[i]=P[1]+P[2]+..+P[i]
SX[i]=X[1]+X[2]+..+X[i]
SXP[i]=X[1]*P[1]+X[2]*P[2]+..+X[i]*P[i]
f[i]=min(f[j]+C[i]+X[i]*(SP[i]-SP[j])-SXP[i]+SXP[j]) (0<=j<i)
f[i]=min(f[j]-X[i]*SP[j]+SXP[j])-SXP[i]+X[i]*SP[i]
f[j]+SXP[j]=X[i]*SP[j]+~~
坐标(SP[j],f[j]+SXP[j])
#include <cstdio> #include <cstdlib> #define ll long long using namespace std; const int N=1e6+10; ll x[N],p[N],sp[N],f[N],sx[N],sxp[N],pre[N],c[N]; int n,l=1,r,q[N]; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%lld%lld%lld",&x[i],&p[i],&c[i]); sx[i]=sx[i-1]+x[i],sp[i]=sp[i-1]+p[i]; sxp[i]=sxp[i-1]+x[i]*p[i]; } for(int i=1;i<=n;i++) { pre[i-1]=f[i-1]+sxp[i-1]; while(l<r && (pre[q[r]]-pre[q[r-1]])*(sp[i-1]-sp[q[r]])>=(pre[i-1]-pre[q[r]])*(sp[q[r]]-sp[q[r-1]])) r--; q[++r]=i-1; while(l<r && pre[q[l+1]]-pre[q[l]]<=x[i]*(sp[q[l+1]]-sp[q[l]])) l++; f[i]=f[q[l]]+c[i]+x[i]*(sp[i]-sp[q[l]])-sxp[i]+sxp[q[l]]; } printf("%lld\n",f[n]); return 0; }
S[i]=X[1]+X[2]+..+X[i]
f[i]=max(f[j]+a*(S[i]-S[j])2+b*(S[i]-S[j])+c) (0<=j<i)
f[i]=max(f[j]-2a*S[i]*S[j]+a*S[j]2-b*S[j])+c+b*S[i]+a*S[i]2
f[j]+a*S[j]2-b*S[j]=2a*S[i]*S[j]+f[i]-c-b*S[i]-a*S[i]2
坐标(S[j],f[j]+a*S[j]2-b*S[j])
注意这里是max,所以要维护一个凸壳,另外决策时也有些不一样(画个图)
#include <cstdio> #include <cstdlib> #define ll long long using namespace std; const int N=1e6+10; ll a,b,c,x[N],pre[N],f[N]; int n,l=1,r,q[N]; ll pf(ll x) { return x*x; } //f[j]+a*S[j]2-b*S[j]=2a*S[i]*S[j]+f[i]-c-b*S[i]-a*S[i]2 int main() { scanf("%d%lld%lld%lld",&n,&a,&b,&c); for(int i=1;i<=n;i++) scanf("%lld",&x[i]),x[i]+=x[i-1]; for(int i=1;i<=n;i++) { pre[i-1]=f[i-1]+a*pf(x[i-1])-b*x[i-1]; while(l<r && (pre[q[r]]-pre[q[r-1]])*(x[i-1]-x[q[r]])<=(pre[i-1]-pre[q[r]])*(x[q[r]]-x[q[r-1]])) r--; q[++r]=i-1; while(l<r && pre[q[l+1]]-pre[q[l]]>=2*a*x[i]*(x[q[l+1]]-x[q[l]])) l++; f[i]=f[q[l]]+a*pf(x[i]-x[q[l]])+b*(x[i]-x[q[l]])+c; } printf("%lld\n",f[n]); return 0; }
斜率优化套路都差不多,但要理解优化原理,以便针对不同情况(如max)
可以考虑斜率优化的式子大概是这个结构:
f([...])[i]=max/min(f[j]+只与j有关的式子+与i,j都有关的式子)+只与i有关的式子