CodeForces-343C Read Time
Read Time
磁带上有n个头,每个头每秒可以向左滑动,或者向右滑动,同时有m个特定的位置需要滑过(n个头初始所在的位置已经被记为滑过),问最少需要多少秒,能够完成任务
二分
非常巧妙的题,由于给出的答案是呈一个01单调分布的,因此可以使用二分搜索答案
难点在于查询是否可行,我们将头从左到右进行验证:
- 如果头的左边没有需要滑过的位置 或者 左边需要滑过的位置已经被滑过:
当前头直接往右边滑就行
- 如果头的左边有需要滑过的位置
-
选择先向左边滑,再尽量往右滑
-
选择先尽量向右边滑,再往左滑
对于上述的两种情况,我们要分析哪种情况能够向右滑更多,就贪心地采用那种情况
#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;
}