Luogu 3957 [NOIP2017]普及组 跳房子

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

首先发现这个$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

猜你喜欢

转载自www.cnblogs.com/CzxingcHen/p/9826578.html