链接
http://www.lydsy.com/JudgeOnline/problem.php?id=1096
题解
1A系列。
用
f[i]
表示在
i
这个点建立仓库的,前i个工厂的货物都能藏起来的最小费用。
显然
其中 w(j,i) 表示把 (j,i] 的货物都运到 i 的费用
令 t[i]=∑ij=1p[j]x[j] , sp[j]=∑ij=1p[j]
w(j,i)=x[i](sp[i]−sp[j])−t[i]+t[j]=−x[i]sp[j]+t[j]+x[i]sp[i]−t[i]
f[i]=c[i]+min{f[j]+w(j,i)}=c[i]+min{−x[i]sp[j]+t[j]+f[j]+x[i]sp[i]−t[i]}
去掉 min 化成斜率优化式子
f[i]=c[i]−x[i]sp[j]+t[j]+f[j]+x[i]sp[i]−t[i]t[j]+f[j]=x[i]sp[j]+(f[i]−c[i]−x[i]sp[i]+t[i])
y=kx+b
yj=t[j]+f[j],ki=x[i],xj=sp[j]
因为要最小化 f[i] ,所以维护下凸壳,斜率 x[i] 是单调不下降的,且点的横坐标 sp[j] 单调不减,所以用队列来维护凸壳。
卡我的地方主要在于,从队尾或队首弹出点的时候,应保证队列中不能为空。
代码
//斜率优化
#include <cstdio>
#include <algorithm>
#define maxn 1000005
#define ll long long
using namespace std;
ll N ,f[maxn], p[maxn], c[maxn], x[maxn], sp[maxn], t[maxn];
struct point{ll x, y, id;}q[maxn];
ll read(ll x=0)
{
char c=getchar();
while(c<48 or c>57)c=getchar();
while(c>=48 and c<=57)x=(x<<1)+(x<<3)+c-48,c=getchar();
return x;
}
void init()
{
ll i;
N=read();
for(i=1;i<=N;i++)x[i]=read(), p[i]=read(), c[i]=read();
for(i=1;i<=N;i++)sp[i]=sp[i-1]+p[i], t[i]=t[i-1]+x[i]*p[i];
}
inline double getk(point p1, point p2)
{
if(p1.x==p2.x)return 1e60;
return 1.0*(p2.y-p1.y)/(p2.x-p1.x);
}
void dp()
{
ll l=1, r=1, i, j;
double k;
point pt;
pt.x=pt.y=pt.id=0;q[r++]=pt;
for(i=1;i<=N;i++)
{
k=x[i];
while(l<r-1 and getk(q[l],q[l+1])<k)l++;
j=q[l].id;
f[i]=c[i]-x[i]*sp[j]+t[j]+f[j]+x[i]*sp[i]-t[i];
pt.x=sp[i], pt.y=t[i]+f[i], pt.id=i;
while(r-l>=2 and getk(pt,q[r-1])<getk(q[r-1],q[r-2]))r--;
q[r++]=pt;
}
}
void print()
{
ll i;
for(i=N;!p[i];i--);
printf("%lld",f[i]);
}
int main()
{
init();
dp();
print();
return 0;
}