题意:A想从0走到n,A每走一单位的距离就要吃一颗糖,现在给出第i个点离0的距离a[i](保证a[i] < a[i+1]),和每个点买入一颗糖和卖出一颗糖的单价buy[i]和sell[i],A能携带糖的上限为C,问从0走到n的最小花费。
如何使得花费最小?低价买入&高价卖出这种情况可以使得花费最小,而且走的顺序是定下来的。。必须从0…1…2…3…n,可以这么做,记录走到每个点都满载的情况花费,然后只需要考虑退油该怎么退,退油的时候减去卖价高的,走到每一个点,对于买入价格小于当前卖价的那些油,都合并成当前卖价买入,对于买入价格大于当前买价的那些油,都以原价退回,这里的“原价”不是一定是原价,而是可以为卖出的价格,将退油的时机改为原价买原价卖改为原价买高价卖。
这些操作需要一个双端队列。。
贴标程。。。。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <set> #include <vector> #include <map> #include <queue> #include <set> #include <algorithm> #include <limits> using namespace std; typedef long long LL; const int MAXN=200000+1000; const int INF = numeric_limits<int>::max(); const LL LL_INF= numeric_limits<LL>::max(); struct que { int val,cnt; }Q[MAXN*2]; int l,r,tot; LL ans; void Max(int v){ int num=0; while(l<=r&&Q[l].val<v){ num+=Q[l].cnt; l++; } if(num){ --l; Q[l].cnt=num; Q[l].val=v; } } void Min(int v){ while(l<=r&&Q[r].val>v){ ans-=1LL*Q[r].val*Q[r].cnt; tot+=Q[r].cnt; --r; } } void Del(int v){ while(v){ int t=min(Q[l].cnt,v); Q[l].cnt-=t; v-=t; if(Q[l].cnt==0)++l; } } int A[MAXN],n,c,sell[MAXN],buy[MAXN]; int main() { int t; scanf("%d",&t); while(t--){ scanf("%d%d",&n,&c); A[0]=0; for(int i=1;i<=n;i++)scanf("%d",&A[i]); for(int i=0;i<=n;i++)scanf("%d%d",&buy[i],&sell[i]); l=r=n; --r; ans=0; for(int i=0;i<n;i++){ Max(sell[i]); tot=(i==0)?c:A[i]-A[i-1]; Min(buy[i]); Q[++r].val=buy[i]; Q[r].cnt=tot; ans+=1LL*buy[i]*tot; Del(A[i+1]-A[i]); } Max(sell[n]); for(int i=l;i<=r;i++)ans-=1LL*Q[i].val*Q[i].cnt; printf("%I64d\n",ans); } return 0; }
|