题意:

一辆车的油箱容量为G(1<=G<=1e6), 车每移动一个单位的距离就要消耗一个单位的油,总共需要走D个单位的距离(1<=D<=1e9)。
除此之外,路上一共有N个加油站,第i个加油站与起点的距离为Xi(0<=Xi<=D),每单位油的价格为Yi(1<=Yi<=1e6)。
一开始你的油箱中有B个单位的油(0<=B<=D),请计算出到达目的地时花费的油费用的最小值。如果无法到达目的地,那么输出-1。

分析:

首先按加油站离起点的距离从小到大排序,那么离起点最近的加油站称为1号加油站,离起点第二近的加油站称为2号加油站,以此类推。
接着用rr[i]表示右边第一个比i号加油站汽油单价小的加油站的下标,rr数组可以使用一个单调栈预处理出来。
接下来进行如下贪心:
使用i从左到右遍历,i为当前的加油站,设b为当前剩余的汽油量,如果b大于等于i号加油站到rr[i]号加油站的距离,则我们可以开车到rr[i]号加油站再加油,这样更赚,因此我们此时不加油;如果b小于i号加油站到rr[i]号加油站的距离,我们继续分类讨论:若我们能把当前汽油加到能正好到rr[i]号加油站,我们就进行这样的加油,到了rr[i]号加油站后再进行后续的加油,这样更赚;若因为油箱大小的限制使得我们无法把汽油加到能正好到rr[i]号加油站,我们索性就把油加满,因为[i,rr[i]-1]区间的汽油单价里,i号加油站的是最小的,因此在此处加满油是最赚的。

代码:

#include<bits/stdc++.h>
using namespace std;

struct node
{
    int x,y;
}R[50005];
bool cmp(node a,node b)
{
    return a.x<b.x;
}
int rr[50005];
int main()
{
    int i,j,n,g,b,d;
    long long ans=0;
    scanf("%d%d%d%d",&n,&g,&b,&d);
    for(i=1;i<=n;i++)scanf("%d%d",&R[i].x,&R[i].y);
    sort(R+1,R+1+n,cmp),R[n+1].x=d,R[0].x=0;
    rr[n]=n+1;
    for(i=n-1;i>=1;i--)
    {
        for(j=i+1;j!=n+1&&R[i].y<=R[j].y;j=rr[j]);
        rr[i]=j;
    }
    for(i=0;i<=n;b-=R[i+1].x-R[i].x,i++)
    {
        if(g<R[i+1].x-R[i].x){printf("-1\n");return 0;}
        if(!i)continue;
        if(b<R[rr[i]].x-R[i].x)
        {
            if(g>=R[rr[i]].x-R[i].x)ans+=(long long)R[i].y*(R[rr[i]].x-R[i].x-b),b=R[rr[i]].x-R[i].x;
            else ans+=(long long)R[i].y*(g-b),b=g;
        }
    }
    printf("%lld\n",ans);
    return 0;
}