题意: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,可以这么做,记录走到每个点都满载的情况花费,然后只需要考虑退油该怎么退,退油的时候减去卖价高的,走到每一个点,对于买入价格小于当前卖价的那些油,都合并成当前卖价买入,对于买入价格大于当前买价的那些油,都以原价退回,这里的“原价”不是一定是原价,而是可以为卖出的价格,将退油的时机改为原价买原价卖改为原价买高价卖。
这些操作需要一个双端队列。。
贴标程。。。。
代码:

//author: CHC
//First Edit Time: 2015-08-17 13:34
#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];
//将买入价大于当前买入价的油都退了,更新答案并计算需要补充的油tot
Min(buy[i]);
//将买入的油数量和单价入队列
Q[++r].val=buy[i];
Q[r].cnt=tot;
ans+=1LL*buy[i]*tot;
//消化从i...i+1这个点的油(最便宜的
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;
}