P2698 [USACO12MAR]花盆Flowerpot

想到了一个二分 + 线段树检验的方法. 就是二分长度, 然后用线段树记录最小&最大值, 暴力扫一遍查询.
但是这样复杂度貌似是 \(O(N(logN)^2)\) 的, 太菜了.

按照以往的经验, 凡是想到这样的思路多半可以用单调队列优化, 一个队列维护最小值, 一个维护最大值就ok.

#include <cstdio>
#include <cstring>
#include <cassert>
#include <iostream>
#include <algorithm>
using namespace std;
typedef pair<int, int> P;
const int MAXN = 100000 + 10;
const int MAXD = 1000000 + 10;
inline int read(){
    char ch = getchar(); int x = 0;
    while(!isdigit(ch)) ch = getchar();
    while(isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
    return x;
}

int N, D;
P a[MAXN], lim[MAXD];
int qmin[MAXD], qmax[MAXD];
inline bool check(int x, int len) {
    int lmin = 1, lmax = 1, rmin = 0, rmax = 0;
    for(int i = 1; i <= len; i++) {
        while(lmin <= rmin && (i - qmin[lmin]) > x) ++lmin;
        while(lmax <= rmax && (i - qmax[lmax]) > x) ++lmax;

        while(lmin <= rmin && lim[qmin[rmin]].first > lim[i].first) --rmin;
        while(lmax <= rmax && lim[qmax[rmax]].second < lim[i].second) --rmax;
        qmin[++rmin] = i, qmax[++rmax] = i;

        if(lim[qmax[lmax]].second - lim[qmin[lmin]].first >= D) return true;
    }
    return false;
}

int main(){
//  freopen("p2698.in", "r", stdin);
    cin>>N>>D;
    for(int i = 1; i <= N; i++) 
        a[i].first = read(), a[i].second = read();
    
    int len = 0;
    for(int i = 1; i <= N; i++) len = max(len, a[i].first);
    for(int i = 1; i <= len; i++) lim[i] = P((1 << 30), 0);
    for(int i = 1; i <= N; i++)
        lim[a[i].first].first = min(lim[a[i].first].first, a[i].second),
        lim[a[i].first].second = max(lim[a[i].first].second, a[i].second);

    int l = 0, r = len + 1;
    while(l < r) {
        int mid = (l + r) >> 1;
        if(check(mid, len)) r = mid;
        else l = mid + 1;
    }
    if(l == len + 1) puts("-1");
    else printf("%d\n", l);
    return 0;
}

posted @ 2018-10-19 09:06  俺是小程  阅读(161)  评论(0编辑  收藏  举报