P2120 [ZJOI2007] 仓库建设 题解
一道很标准的斜率 dp
简要题意
有
转移方程
(对于最基础的
首先,因为对于工厂
然后照常把
令
将只与
即:
接下来就是套斜率 dp 板子了
本题需要求费用即
时间复杂度
小细节
- hack 数据中末尾会有一连串
p=0 的工厂,不会对答案产生任何贡献(他们无需运送货物,也不会被作为仓库),但是 dp 过程中会把他们算进去,因此答案应当取最后一个有货物的工厂作为仓库时的最小花费。 - 我们斜截式中的
x 是可能重复的,因此要判断斜率是极大值还是极小值,以避免弹出最优解或避开最优解。我不说是谁没调导致交了 5 个红的 100pts...
CODE
//请千万不要试图直接提交此代码!
/*
f[i] = min( f[k] + c_i + sigma( p_p * dis_p..i ) ){i>=p>k}
= f[k] + c_i + sigma( p_p * ( x_i - x_p ) )
= f[k] + c_i - sigma( p_p * x_p ) + x_i * sigma( p_p )
let sumt_i = sigma( p_i * x_i ) , s_i = sigma( p_i );
=> f[i] = f[k] + c_i - ( sumt[i] - sumt[k] ) + x_i * ( s[i] - s[k] );
= f[k] + c_i - sumt[i] +sumt[k] + x_i * s[i] - x_i * s[k];
=> f[k] + sumt[k] = x_i * s[k] + f[i] - c_i + sumt[i] - x_i * s[i];
~~~~~~~~~~~~ ~~~ ~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
y = k * x + b
*/
const int N=1e6+5;
long long n;
long long x[N],p[N],c[N];
long long sumt[N],sump[N];
long long f[N];
long long q[N];
int l,r;
double Y(int i){
return f[i] + sumt[i];
}
double X(int i){
return sump[i];
}
double slope(int i,int j){
if (X(i)==X(j)) return (Y(i)-Y(j))>0?-1e18:1e18;
return (Y(i)-Y(j))/(X(i)-X(j));
}
signed mаin(){
scаnf("%lld",&n);
for (int i=1;i<=n;i++) scаnf("%lld%lld%lld",x+i,p+i,c+i);
for (int i=1;i<=n;i++) sumt[i]=p[i]*x[i] + sumt[i-1], sump[i]= p[i] + sump[i-1];
l=r=1;
for (int i=1;i<=n;i++){
while(l<r&&slope(q[l],q[l+1])<=x[i]) l++;
int k=q[l];
f[i]=f[k] + c[i] - ( sumt[i] - sumt[k] ) + x[i] * ( sump[i] - sump[k] );
while(l<r&&(slope(q[r],q[r-1])>=slope(q[r],i))) r--;
q[++r] = i;
}
long long аns=0x3f3f3f3f3f3f;
for(int i=n;i>=1;i--){
аns=min(аns,f[i]);
if(p[i])breаk;
}
printf("%lld\n",аns);
return QWQ;
}