• 攀爬 [二分, 思维]


    攀爬



    color{red}{正解部分}

    若存在一个合法的攀爬序列, 则其形式一定是 ak+i=1m(aibi)La_{k} + sumlimits_{i=1}^m (a_i - b_i) geq L,
    于是考虑枚举 aka_k, 设 di=aibid_i = a_i-b_i, 按 dd 从大到小 排序, 然后 O(N)O(N) 解出 爬出井 的最早时间, 更新答案, 总时间复杂度 O(N2)O(N^2) .

    考虑怎么优化, 记 sumi=j=1idjsum_i = sumlimits_{j=1}^i d_j, 设最早被淹死的时间为 dayday, day=min(i  sumij=1icj)day = min(i | sum_i le sumlimits_{j=1}^i c_j),
    只需要在 sumi,i[1,day]sum_i, i ∈ [1, day]二分查找 LakL - a_k 即可得到 关于 aka_k 的答案,

    但是随着 aka_k 的变化, sumisum_i 同样也在变化, 我们要使得这种变化更加 “平滑”, 更加易于控制一点,

    于是我们将 aka_k 从右往左 枚举, 考虑取出 aka_k 会对哪些 sumisum_i 造成影响,
    会对 sump,p[k,n]sum_p, p ∈ [k, n] 造成影响, 这种变化会使得 sump=sump+di+1dksum^{'}_p = sum_p + d_{i+1} - d_k,

    由于 dkd_k 从右向左 枚举, 且 {di}{d_i} 是 递减的, 所以 dkd_k 是不断增大的, 进而 sumpsum^{'}_p 是不断减少的,
    所以 dayday 只有可能不断向左移动, 而向左移动最多移动 NN 步, 所以按上述方法暴力移动 dayday, 总复杂度是 O(NlogN)O(N log N) 的 .


    但是这里要提到的是, 当出现 di<0d_i < 0 的情况时, sumi{sum_i} 并非单调,
    为了应对这种情况, 需要找出一个中准点 mdmd, 使得 [1,md][1, md] 是单调递增的, 在 [1,min(day,md)][1, min(day, md)]二分查找 即可 .


    color{red}{实现部分}

    • 注意一步就能跳到井口的情况需要特判 .
    #include<bits/stdc++.h>
    #define reg register
    typedef long long ll;
    
    int read(){
            char c;
            int s = 0, flag = 1;
            while((c=getchar()) && !isdigit(c))
                    if(c == '-'){ flag = -1, c = getchar(); break ; }
            while(isdigit(c)) s = s*10 + c-'0', c = getchar();
            return s * flag;
    }
    
    const int maxn = 100005;
    
    int N;
    int L;
    
    ll C[maxn];
    ll sum[maxn];
    
    struct Pill{ int a, b, d; } A[maxn];
    
    bool cmp(Pill x, Pill y){ return x.d > y.d; }
    
    int lower_bound(int l, int r, const int &aim, const int &k){
            int res = -1;
            while(l <= r){
                    int mid = l+r >> 1;
                    ll tmp = sum[mid] + ((mid>=k)?(-A[k].d+A[mid+1].d):0);
                    (tmp >= aim) ? r = mid - 1, res = mid : l = mid + 1;
            }
            return res;
    }
    
    int main(){
            N = read(), L = read();
            for(reg int i = 1; i <= N; i ++) A[i].a = read(), A[i].b = read(), A[i].d = A[i].a - A[i].b;
            std::sort(A+1, A+N+1, cmp);
            for(reg int i = 1; i <= N; i ++) C[i] = C[i-1] + read();
            int day = N;
            for(reg int i = 1; i <= N; i ++){
                    sum[i] = sum[i-1] + A[i].d;
                    if(sum[i] <= C[i]) day = std::min(day, i);
                    if(sum[i] < sum[i-1] && (i-1)) day = std::min(day, i);
            }
            int Ans = -1;
            for(reg int k = N; k >= 1; k --){
                    if(day >= k){ 
                            ll d = sum[day] - A[k].d + A[day+1].d;
                            while(day >= k && d <= C[day]) day --, d = sum[day]-A[k].d+A[day+1].d;
                    }
                    int pos = lower_bound(1, day, L-A[k].a, k);
                    if(pos != -1){ if(Ans == -1) Ans = pos; else Ans = std::min(Ans, pos); }
            }
            if(Ans != -1) Ans ++;
            if(Ans == 2) Ans --;
            printf("%d
    ", Ans);
            return 0;
    }
    
  • 相关阅读:
    假期周总结报告(五)
    java 原码反码及补码 总结
    java ==与equals()方法的总结
    java String 类型总结
    java 全局变量 的小结
    java static语句的总结
    ATM开学测试(未完成)
    第七周学习进度总结
    第六周学习进度总结
    第五周学习进度总结
  • 原文地址:https://www.cnblogs.com/zbr162/p/11822449.html
Copyright © 2020-2023  润新知