讲一个比较呆一点的做法:
设f[i][1]表示后i个点且i点建仓库的最优解,f[i][0]表示后i个点且i点不建仓库的最优解。
那么显然可以从后往前DP:
f[i][0]=mini<j<=n{f[j][1]+∑i<=k<j(X[j]-X[k])*P[j]};
f[i][1]=mini<j<=n{f[i+1][0],f[i+1][1]}+C[i];
令S[j]=∑i<=k<j(X[j]-X[k])*P[k]。
则S[j]=∑i<=k<j(X[j]*P[k]-X[k]*P[k])=∑i<=k<jX[j]*P[k]-∑i<=k<jX[k]*P[k]=X[j]*∑i<=k<jP[k]-∑i<=k<jX[k]*P[k]。
不妨令A[x]=X[x]*P[x]。
那么S[j]可以用后缀和表示为:S=X[j]*(sumP[i]-sumP[j])-(sumA[i]-sumA[j])。
所以f[i][0]=min{f[j][1]+X[j]*(sufP[i]-sufP[j])-(sufA[i]-sufA[j])} 。
把式子化成一次函数可得:
f[j]-X[j]*sufP[j]+sufA[j]=-sufP[i]*X[j]+sufA[i]+f[i]
从后往前维护一个斜率单调递减的下凸壳即可。
需要注意的是,每次应先转移好fi。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> #define RG register #define IL inline #define DB double #define int long long using namespace std; IL int gi() { RG int x=0,w=0; char ch=0; while (ch<'0'||ch>'9') {if (ch=='-') w=1;ch=getchar();} while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar(); return w?-x:x; } const int N=1e6+10; int l,r,q[N]; int n,P[N],X[N],C[N],A[N],sufA[N],sufP[N],f[N][2]; IL DB Y(int x) {return (DB)f[x][1]-X[x]*sufP[x]+sufA[x];} IL DB slope(int x,int y) {return (Y(x)-Y(y))/(DB)(X[x]-X[y])*1.0;} signed main () { RG int i,j; n=gi(); for (i=1;i<=n;++i) X[i]=gi(),P[i]=gi(),C[i]=gi(),A[i]=P[i]*X[i]; for (i=n;i>=1;--i) sufP[i]=sufP[i+1]+P[i],sufA[i]=sufA[i+1]+A[i]; memset(f,0x3f,sizeof(f)); l=r=1,q[1]=n,f[n][1]=C[n]; for (i=n-1;i>=1;--i) { f[i][1]=min(f[i+1][0],f[i+1][1])+C[i]; /*for (j=i+1;j<=n;++j) f[i][0]=min(f[i][0],f[j][1]+X[j]*(sufP[i]-sufP[j])-(sufA[i]-sufA[j])); //f[j][1]-X[j]*sufP[j]+sufA[j]=-sufP[i]*X[j]+sufA[i]+f[i][0];*/ while (l<r&&slope(q[l],q[l+1])>=(DB)-sufP[i]) ++l; f[i][0]=f[q[l]][1]+X[q[l]]*(sufP[i]-sufP[q[l]])-(sufA[i]-sufA[q[l]]); while (l<r&&slope(i,q[r])>=slope(q[r],q[r-1])) --r; q[++r]=i; } printf("%lld ",min(f[1][1],f[1][0])); return 0; }