I - Colonial Mansions Gym - 101962I[二分+线段树]

题意:给出一个有n个数字的数列,有两种操作

  1. 1 x H 将x位置的数字改为H
  2. 2 x H 从x出发在每次数字相差不超过H的条件下可以向左右拓展多少个

思路:可以维护一个差分序列,这里维护的是相邻两个数字的差值(为了条件2),然后通过线段树去维护这个差值,当更新的时候需要更新两个差值,i - 1和i,因为更改i位置的数字会影响它本身与后面的差值,同样会影响他前面数字与它的差值,所以每次更新需要线段树两次单点更新,查询向左右分别拓展了多少的时候可以二分,因为距离具有单调性,查询某个区间的最大值(差分后为最大差值),如果这个最大值满足条件再拓展区间即可。复杂度O( n l o g n l o g n nlognlogn nlognlogn)。


a c   c o d e : ac\ code: ac code:

#include 

using namespace std;
typedef long long ll;
#define met(a, b) memset(a, b, sizeof(a))
#define rep(i, a, b) for(int i = a; i <= b; i++)
#define per(i, a, b) for(int i = a; i >= b; i--)
const int maxn = 1e6 + 10;
const int inf = 0x3f3f3f3f;
#define int ll
struct TreeNode {
    int l, r, MAX, MIN;
} treeNode[maxn << 2];

int a[maxn], hh[maxn];

void push_up(int root) {
    treeNode[root].MAX = max(treeNode[root << 1].MAX, treeNode[root << 1 | 1].MAX);
    treeNode[root].MIN = min(treeNode[root << 1].MIN, treeNode[root << 1 | 1].MIN);
}

void buildTree(int root, int l, int r) {
    treeNode[root].l = l;
    treeNode[root].r = r;
    if(l == r) {
        treeNode[root].MAX = treeNode[root].MIN = hh[l];
        return;
    }
    int mid = (l + r) >> 1;
    buildTree(root << 1, l, mid);
    buildTree(root << 1 | 1, mid + 1, r);
    push_up(root);
}

void upd(int root, int pos, int x) {
    if(treeNode[root].l == treeNode[root].r) {
        treeNode[root].MAX = treeNode[root].MIN = x;
        return;
    }
    int mid = (treeNode[root].l + treeNode[root].r) >> 1;
    if(pos <= mid) upd(root << 1, pos, x);
    else upd(root << 1 | 1, pos, x);
    push_up(root);
}

int MAX, MIN;

void query(int root, int l, int r) {
    if(l > r) {
        MAX = 0;
        return;
    }
    if(treeNode[root].l == l && treeNode[root].r == r) {
        MAX = max(MAX, treeNode[root].MAX);
        MIN = min(MIN, treeNode[root].MIN);
        return;
    }
    int mid = (treeNode[root].l + treeNode[root].r) >> 1;
    if(r <= mid) query(root << 1, l, r);
    else if(l > mid) query(root << 1 | 1, l, r);
    else {
        query(root << 1, l, mid);
        query(root << 1 | 1, mid + 1, r);
    }
}

int divide(int l, int r, int flag, int x, int h) {
    int res = 0;
    while(l <= r) {
        int mid = (l + r) >> 1;
        MAX = -inf; MIN = inf;
        if(flag == 0) query(1, mid, x - 1);
        else query(1, x, mid - 1);
        if(MAX <= h) {
            if(flag == 0) {
                res = max(x - mid, res);
                r = mid - 1;
            } else {
                res = max(mid - x, res);
                l = mid + 1;
            }
        } else {
            if(flag == 0) {
                l = mid + 1;
            } else {
                r = mid - 1;
            }
        }
    }
    return res;
}
#undef int
int main() {
    #define int ll
    int n, q;
    while(~scanf("%lld%lld", &n, &q)) {
        rep(i, 1, n) scanf("%lld", &a[i]);
        rep(i, 1, n) {
            hh[i] = abs(a[i] - a[i + 1]);
        }
        hh[n] = 0;
        buildTree(1, 1, n);
        int com, x, h;
        while(q--) {
            scanf("%lld%lld%lld", &com, &x, &h);
            if(com == 1) {
                a[x] = h;
                hh[x] = abs(a[x] - a[x + 1]);
                upd(1, x, hh[x]);
                if(x - 1 > 0) {
                    hh[x - 1] = abs(a[x - 1] - a[x]);
                    upd(1, x - 1, hh[x - 1]);
                }
            } else {
                int ans = 1;
                ans += divide(x, n, 1, x, h);
                ans += divide(1, x, 0, x, h);
                printf("%lld\n", ans);
            }
        }
    }
    return 0;
}

你可能感兴趣的:(线段树,二分答案)