hdu5380 贪心+双端队列

http://acm.hdu.edu.cn/showproblem.php?pid=5380

Problem Description
There are n+1 cities on a line. They are labeled from city 0 to city n. Mph has to start his travel from city 0, passing city 1,2,3...n-1 in order and finally arrive city n. The distance between city i and city 0 is  ai . Mph loves candies and when he travels one unit of distance, he should eat one unit of candy. Luckily, there are candy shops in the city and there are infinite candies in these shops. The price of buying and selling candies in city i is  buyi  and  selli  per unit respectively. Mph can carry at most C unit of candies.
Now, Mph want you to calculate the minimum cost in his travel plan.
 

Input
There are multiple test cases.
The first line has a number T, representing the number of test cases.
For each test :
The first line contains two numbers  N  and  C   (N2×105,C106)
The second line contains  N  numbers  a1,a2,...,an . It is guaranteed that  ai>ai1  for each  1<i<=N  .
Next  N+1  line : the i-th line contains two numbers  buyi1  and  selli1  respectively. ( selli1buyi1106 )

The sum of  N  in each test is less than  3×105 .
 

Output
Each test case outputs a single number representing your answer.(Note: the answer can be a negative number)
 

Sample Input
  
  
1 4 9 6 7 13 18 10 7 8 4 3 2 5 4 5 4
 

Sample Output
  
  
105
/**
hdu5380 贪心+双端队列
题目大意:一个人从0走到n知道ai为i节点到0的距离,没行走单位距离要消耗一颗糖,在所有节点中可以进行买糖和卖糖价格为sell[i]和buy[i],问走到n节点话费最小为多少
解题思路:从0开始,每次都把当前携带的糖的数量为C,到下一个节点,如果卖的价格高的话就把当前口袋里剩的价钱较低买的换成当前点卖的价格(因为当前剩的糖是多余的
           走到最后是要被退掉的,所以我们此举把退的价格抬高了),然后把前一个节点到当前节点路上消耗的糖在现在买回来,保持携带糖为C,走到n后把所有剩的糖退掉
           思想有点难理解,需要好好想一想,贴上标程
*/
#include <iostream>
#include <cstdio>
#include <string.h>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN=200000+1000;
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;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值