首页 > 其他 > 详细

Luogu 3957 [NOIP2017]普及组 跳房子

时间:2018-10-21 20:31:39      阅读:158      评论:0      收藏:0      [点我收藏+]

写了好久,感觉自己好菜,唉……

首先发现这个$g$的取值具有单调性,可以想到二分答案,然后考虑用$dp$来检验,这样子可以写出朴素的转移方程:

  设$f_i$表示以$i$结尾的最大价值,那么有$f_i = max(f_j) + val_i$  $(0 < j < i)$  $((dis_i - (d + g) \leq dis_j \leq dis_i  - max(d - g, 1)))$。

然后注意到是选取一个滑动窗口的最大值,用一个单调队列优化一下就可以了。

时间复杂度$O(nlogn)$。

注意开$long\ long$。

Code:

技术分享图片
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;

const int N = 5e5 + 5;
const ll inf = 1LL << 60;

int n, d, dis[N], q[N];
ll cur, val[N], f[N];

template <typename T>
inline void read(T &X) {
    X = 0; char ch = 0; T op = 1;
    for(; ch > 9 || ch < 0; ch = getchar())
        if(ch == -) op = -1;
    for(; ch >= 0 && ch <= 9; ch = getchar())
        X = (X << 3) + (X << 1) + ch - 48;
    X *= op;
}

template <typename T>
inline void chkMax(T &x, T y) {
    if(y > x) x = y;
}

template <typename T>
inline int max(T x, T y) {
    return x > y ? x : y;
}

inline bool chk(int mid) {
    int st = max(1, d - mid), ed = d + mid, l = 1, r = 0;
    memset(f, 0LL, sizeof(f));
    for(int j = 0, i = 1; i <= n; i++) {        
        for(; j < i && dis[j] <= dis[i] - st; j++) {
            for(; l <= r && f[q[r]] < f[j]; --r);
            q[++r] = j;
        }

        for(; l <= r && dis[q[l]] < dis[i] - ed; ++l);
        ll mx = f[q[l]];
        if(l > r) mx = -inf;
        f[i] = mx + val[i];        
        
        if(f[i] >= cur) return 1;
    }
    return 0;
}    

/*inline bool chk(int mid) {
    int st = max(1, d - mid), ed = d + mid;
    memset(f, 0, sizeof(f));
    for(int i = 1; i <= n; i++) {
        int res = -inf;
        for(int j = 0; j < i; j++)
            if(dis[j] >= dis[i] - ed && dis[j] <= dis[i] - st)
                chkMax(res, f[j]);
        f[i] = res + val[i];
        if(f[i] >= cur) return 1;
    }
    return 0;
}   */

int main() {
    read(n), read(d), read(cur);
    int mn = 0; ll sum = 0;
    for(int i = 1; i <= n; i++) {
        read(dis[i]), read(val[i]);
        chkMax(mn, dis[i]);
        if(val[i] > 0) sum += val[i];
    }
    
    if(sum < cur) return puts("-1"), 0;

    int ln = 0, rn = mn, mid, res = -1;
    for(; ln <= rn; ) {
        mid = (ln + rn) / 2;
        if(chk(mid)) rn = mid - 1, res = mid;
        else ln = mid + 1;
    }   

    printf("%d\n", res);
    return 0;
}
View Code

 

Luogu 3957 [NOIP2017]普及组 跳房子

原文:https://www.cnblogs.com/CzxingcHen/p/9826578.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!