Loading

CodeForces-343C Read Time

Read Time

磁带上有n个头,每个头每秒可以向左滑动,或者向右滑动,同时有m个特定的位置需要滑过(n个头初始所在的位置已经被记为滑过),问最少需要多少秒,能够完成任务

二分

非常巧妙的题,由于给出的答案是呈一个01单调分布的,因此可以使用二分搜索答案

难点在于查询是否可行,我们将头从左到右进行验证:

  1. 如果头的左边没有需要滑过的位置 或者 左边需要滑过的位置已经被滑过:

当前头直接往右边滑就行

  1. 如果头的左边有需要滑过的位置
  • 选择先向左边滑,再尽量往右滑

  • 选择先尽量向右边滑,再往左滑

对于上述的两种情况,我们要分析哪种情况能够向右滑更多,就贪心地采用那种情况

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string>
#include <queue>
#include <functional>
#include <map>
#include <set>
#include <cmath>
#include <cstring>
#include <deque>
#include <stack>
using namespace std;
typedef long long ll;
#define pii pair<int, int>
const ll maxn = 1e5 + 10;
const ll inf = 1e17 + 10;
ll h[maxn], p[maxn];
int n, m;

bool query(ll x)
{
    ll pre = 0, tp = 0;
    for(int i=0; i<n && tp<m; i++)
    {
        pre = h[i];
        if(p[tp] <= h[i]) pre = p[tp];
        if(h[i] - pre > x) return false;
        ll dis = h[i] - pre;
        ll nex = max((x - dis) / 2, x - 2 * dis);
        nex += h[i];
        while(tp < m && p[tp] <= nex) tp++;
    }
    return tp == m;
}


int main()
{
    scanf("%d%d", &n, &m);
    for(int i=0; i<n; i++)
        scanf("%lld", &h[i]);
    for(int i=0; i<m; i++)
        scanf("%lld", &p[i]);
    ll l = 0, r = p[m-1] + h[n-1];
    while(l < r)
    {
        ll mid = l + r >> 1;
        if(query(mid))
            r = mid;
        else
            l = mid + 1;
    }
    printf("%lld\n", r);
    return 0;
}
posted @ 2022-04-25 18:15  dgsvygd  阅读(20)  评论(0编辑  收藏  举报