[ZJOI2007]仓库建设

传送门

考虑用dp[i]表示把前i个地点的物品全部安置好的最小花费。因为物品只能往下运,所以当前这个位置必须建仓库,dp方程很好想:

\[dp[i] = min_{j=1}^{i-1}\{dp[j] + \sum_{k=j+1}^{i-1} p[k] * (x[i] - x[k])\} + c[i] \]

\(sum[n]\)表示\(\sum_{i=1}^np[i]\),\(ssum[n]\)表示\(\sum_{i=1}^np[i] * x[i]\)

之后把式子变个型套斜率优化就好了。得到:

\(\frac{dp[p]-dp[q]+ssum[p]-ssum[q]}{sum[p]-sum[q]} < x[i]\)时,q比p优。

因为这个x[i]是单调递增的,所以不用考虑特别多,套斜率优化即可。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')
#define pr pair<int,int>
#define mp make_pair
#define fi first
#define sc second
using namespace std;
typedef long long ll;
const int M = 1000005;
const int N = 10000005;
 
ll read()
{
   ll ans = 0,op = 1;char ch = getchar();
   while(ch < '0' || ch > '9') {if(ch == '-') op = -1;ch = getchar();}
   while(ch >='0' && ch <= '9') ans = ans * 10 + ch - '0',ch = getchar();
   return ans * op;
}

struct node
{
   ll x,p,c;
}a[M];

ll sum[M],ssum[M],dp[M],n,q[M],head,tail;

double slope(ll p,ll q)
{
   return (double)((dp[p]-dp[q]+ssum[p]-ssum[q]) / (sum[p]-sum[q]));
}

int main()
{
   n = read(),head = tail = 1;
   rep(i,1,n) a[i].x = read(),a[i].p = read(),a[i].c = read();
   rep(i,1,n) sum[i] = sum[i-1] + a[i].p,ssum[i] = ssum[i-1] + a[i].x * a[i].p;
   rep(i,1,n)
   {
      while(head < tail && slope(q[head],q[head+1]) < a[i].x) head++;
      dp[i] = dp[q[head]] + (sum[i] - sum[q[head]]) * a[i].x + ssum[q[head]] - ssum[i] + a[i].c;
      while(head < tail && slope(q[tail-1],q[tail]) > slope(q[tail],i)) tail--;
      q[++tail] = i;
   }
   printf("%lld\n",dp[n]);
   return 0;
}

posted @ 2018-12-21 00:07  CaptainLi  阅读(233)  评论(0编辑  收藏  举报