hdu5380 貪心+雙端佇列

life4711發表於2015-08-18

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;
}


相關文章