car (CF 581E)

题目描述

Keith买了一辆新车,打算把自己的新车开到学校,给403的小伙伴们看看。
Keith家(多个)在城区,从城区到学校的路可以抽象成一个数轴,Keith家的坐标为W,学校的坐标为E。路上有很多加油站,每个加油站能提供92#,95#,98#三种汽油中的一种。由于Keith不缺钱,每到一个加油站,他都能加任意多的油。由于道路是双向的,Keith的车既能往左开,也能往右开。
1升汽油可以跑1千米,这与油的种类无关。车的油箱容量是S升,任何时候油箱中的油都不能超过S升,但是作为Keith大神的御用座驾,这台车有点与众不同,它的油箱中能同时存在多种汽油。出发时,油箱是装满98#汽油的。
众所周知的是,98#汽油最好,92#最差。由于Keith很爱惜自己的新车,所以他希望用尽可能少的92#汽油,如果有多种可能用的92#同样多,那就要求用的95#尽可能少。
现在Keith告诉你N个家的坐标Wi,学校的坐标E,以及路上所有加油站的坐标,求从各个家分别到学校的最优策略中,需要消耗的最少92#和95#汽油。
由于Keith忙于研究SAM及其相关应用,所以他找到你来帮他完成这个任务。
第i个加油站坐标是Xi,油的种类是Ti

数据范围1 ≤ N , M ≤ 200000 , 0 ≤ Wi , Xi , E , S ≤ 10^9且Wi ≤ E , 1 ≤ Ti ≤ 3

贪心

显然,(性质1)车只会向右开,因为Wi ≤ E。
另外,(性质2)从车站i出发,开到满足t[j]>=t[i]的最近的一个车站j一定是最优的(最优不一定唯一,但肯定不存在更优)。
根据性质1
首先我们可以把车站根据坐标排序(从小到大),把坐标大于等于E的车站忽略(因为没有贡献),然后把学校视为坐标等于E且T=3的车站。
我们开3个vector,T=3放进vector3,T>=2放进vector1~2,T>=1放进vecotor1(只要满足都需要放进)
这个可以用于二分查找来优化dp

dp

设f[i][j]表示从第i个车站只通过T值>=j的车站到达学校至少需要增加多少油
当f[i][j]>0时就表示i不能只通过T值>=j的车站到达学校。
j只需从1枚举到T[i,因为有性质2。
然后如果一个家Wi能够到达学校,当且仅当从Wi到达的第一个T值>=1的车站i,满足f[i][0]+max(Xi-Wi-s,0)=0;
设从Wi到达的第一个T值>=2的车站为i1,从Wi到达的第一个T值=2的车站为i2
则92#汽油的最小消耗为s1=f[i1][0]+max(Xi1-Wi-S,0),95#汽油的最小消耗为f[i2][0]+max(Xi2-Wi-S,0)-s1

代码

#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=200000+100;
struct ar{
    int x,y;
    friend bool operator <(ar x,ar y) {
        return x.x<y.x;
    }
} p[maxn];
int i,j,n,m,e,s,w[maxn];
int f[maxn][3];
vector<ar> a[3];
bool cmp(ar x,ar y){
    return x.y<y.y;
}
int main(){
    scanf("%d%d%d%d",&e,&s,&n,&m);
    fo(i,1,n) {scanf("%d%d",&p[i].x,&p[i].y);p[i].x--;}
    sort(p+1,p+1+n,cmp);
    ar w;
    fo(i,1,n) if (p[i].y<e)fo(j,0,p[i].x) {
        w.x=p[i].y,w.y=i;
        a[j].push_back(w);
    }else {n=i-1;break;}
    w.x=e,w.y=n+1;
    fo(j,0,2) a[j].push_back(w);
    fod(i,n,1){
        w.x=p[i].y;
        ar t=*upper_bound(a[0].begin(),a[0].end(),w);
        f[i][0]=f[t.y][0]+max(t.x-w.x-s,0);
        if (p[i].x>0){
            t=*upper_bound(a[1].begin(),a[1].end(),w);
            f[i][1]=f[t.y][1]+max(t.x-w.x-s,0);
            if (p[i].x==2){
                t=*upper_bound(a[2].begin(),a[2].end(),w);  
                f[i][2]=f[t.y][2]+max(t.x-w.x-s,0);
            }
        }
    }
    fo(i,1,m){
        scanf("%d",&w.x);if (w.x==e) {printf("0 0\n");continue;}
        ar t=*upper_bound(a[0].begin(),a[0].end(),w);
        int g=f[t.y][0]+max(t.x-w.x-s,0);
        if (g) {printf("-1 -1\n");continue;}
        t=*upper_bound(a[1].begin(),a[1].end(),w);int t1=f[t.y][1]+max(t.x-w.x-s,0);
        printf("%d ",t1);
        t=*upper_bound(a[2].begin(),a[2].end(),w);
        printf("%d\n",f[t.y][2]+max(t.x-w.x-s,0)-t1);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值