Description

某人在起点处,到终点的距离为 \(s\)。汽车租赁公司提供 \(n\) 种车型,每种车型有属性 \(c_i\)(租车费用),\(v_i\)(油箱容量)。车子有两种前进方式:①慢速:\(1km\) 消耗 \(1L\) 汽油,花费 \(2\) 分钟。②快速:\(1km\) 消耗 \(2L\) 汽油,花费 \(1\) 分钟。路上有 \(k\) 个加油站,油都是免费的,加油不需要花费时间,且直接给油箱加满。问在 \(t\) 分钟内到达终点的最小花费是多少?若无法到达终点,输出 \(-1\)

Solution

考虑对于一段长为 \(len\) 的区间,设慢速 \(x\),快速 \((len-x)\),则

\[t=2x+len-x=x+len \\ v=x+2(len-x)=2len-x \Rightarrow x=2len-v \\ 0 \le x \le len \]

如果直接求解得出的 \(x > len\) 则说明无法通行,如果直接求解的 \(x <0\),我们设其为 \(0\) 即可

对所有的 \(t\) 求和,如果比给定的开始时间小,则可行

这样我们就得到了一个 \(O(nk)\) 的算法

考虑到费用本身并没有什么用,所以我们只需要二分出一个最小的,可以完成任务的 \(v\),然后对于所有 \(v_i \ge v\) 对应的 \(c_i\) 取最小值即可

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

#define int long long
const int N = 1000005;

int s,t,n,c[N],v[N],k,g[N];

signed main() {
    ios::sync_with_stdio(false);
    cin>>n>>k>>s>>t;
    for(int i=1;i<=n;i++) cin>>c[i]>>v[i];
    for(int i=1;i<=k;i++) cin>>g[i];
    sort(g+1,g+k+1);
    g[k+1]=s;
    int l=1,r=2e9;
    while(l<r) {
        int mid=(l+r)/2;
        int sum=0;
        int flag=1;
        for(int i=1;i<=k+1;i++) {
            int len=g[i]-g[i-1];
            int x=2*len-mid;
            if(x>len) {
                flag=0;
                break;
            }
            x=max(x,0ll);
            sum+=x+len;
        }
        if(sum>t) flag=0;
        if(flag) r=mid;
        else l=mid+1;
    }
    int ans=1e18;
    for(int i=1;i<=n;i++) if(v[i]>=r) ans=min(ans,c[i]);
    if(ans==1e18) cout<<-1;
    else cout<<ans;
}

相关文章: