Codeforces - 343C 二分答案

題意

給出\(n\)個指針的座標\(h_i\),以及\(m\)個地址的座標\(p_i\),每一秒指針均可以移動一個單位,求最少的時間使得全部地址都被遍歷ios

範圍

\(1 \le n,m \le 10^5,1 \le h_i,p_i \le 10^{10}\)c++

分析

老實說我當時搞不出來,看了tutorial以爲方法很不錯spa

首先你們都知道的是排序(已省略)並二分答案\(mid\),指針

而後從左到右考慮每個地址被覆蓋的狀況(重要)code

咱們指望枚舉過程當中作到的是排序

  • 保留儘量多的指針
  • 讓每一個指針跑的儘量遠

對於條件1,顯然當該地址存在相對於它的左右邊都有指針且可被覆蓋時,優先選左端ci

對於條件2且只剩下右端點能夠選擇時,找能用的最靠近它的端點,這個時候有兩種作法it

  • 要麼先往左跑,遍歷該地址後儘可能往右跑
  • 要麼先儘可能往右跑,最後對該地址踩點便可

依此列出式子而後比較一下選擇最優的策略(踩點包含了右端點沒法擴展的極端狀況)io

寫起來特簡潔,這道題真是喵啊喵啊class

實現

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int MAXN = 1e5+11;
int n,m;
ll p[MAXN],h[MAXN];
bool C(ll mid){
    int i=1,j=1;
    while(j<=m){
        while(h[i]+mid<p[j]&&i<=n) i++;
        if(i>n) return 0;
        if(h[i]<=p[j]){
            while(h[i]+mid>=p[j]&&j<=m) j++;
            i++;
        }else if(mid>=h[i]-p[j]){
            ll t=max(mid-2ll*(h[i]-p[j]),(mid-(h[i]-p[j]))/2)+h[i];
            while(t>=p[j]&&j<=m) j++;
            i++;
        }else return 0;
    }
    return 1;
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    while(cin>>n>>m){
        for(int i=1;i<=n;i++) cin>>h[i];
        for(int i=1;i<=m;i++) cin>>p[i];
        ll lo=0,hi=2e10;
        while(lo<hi){
            ll mid=lo+(hi-lo)/2;
            if(C(mid)) hi=mid;
            else lo=mid+1;
        }
        cout<<lo<<endl;
    }
    return 0;
}
相關文章
相關標籤/搜索