I - Colonial Mansions Gym - 101962I(线段树 + 二分)

After a year of hard work, Mano finally had a month off. He decided to take his grandmother to Soteropolis during this time. As soon as they arrived at the airport, Mano bought one of these "10 things to do in Soteropolis" travel guides. His grandmother instantly felt in love with pictures of the colonial mansions of Pelourinho, a historic neighborhood in western Soteropolis.

Pelourinho is a very big neighborhood. It is full of slopes and can be very trick for an elderly lady to explore. We can simplify its structure by imagining it as a line. The colonial mansions can be imagined as equally spaced spots on these lines: that is, from the i

-th mansion (from left to right), you can go to mansions i−1 and i+1, if such mansions exist. These mansions are called neighbors of mansion i. Also, the i-th mansion is hi

meters above sea level.

Mano knows that his grandmother will have serious difficulties in this kind of terrain. Your task is to write a computer program to answer some queries and help Mano plan his trip to Pelourinho. The two types of queries are described below:

  • 1 i

H: the height of the i-th mansion should be set to H

  • ;
  • 2 i
  • H
  • : how many mansions Mano and his grandmother can visit if they start their trip at mansion i and his grandmother can handle going from a mansion u to a neighbor mansion v iff |hu−hv|≤H
    • ?

    Write a program to process these queries.

    Input

    The first line of the input contains two integers n

    and q (1≤n≤105; 1≤q≤105

    ) - the number of colonial mansions and the number of queries you are asked to process, respectively.

    The second line of the input contains n

    integers. The i-th of them is hi (0≤hi≤109) – the height above the sea level of the i

    -th mansion.

    The next q

    lines of the input will each describe a query, which will have the format shown in the statement (1≤i≤n; 0≤H≤109

    ).

    Output

    For each operation of type 2, print a line with an integer – the number of colonial mansions Mano and his grandmother can visit in such scenario.

    The queries should be processed in the order they are given in the input file.

    Examples

    Input

    4 3
    1 2 1 3
    2 2 1
    1 4 1
    2 3 1
    

    Output

    3
    4
    

    Input

    4 5
    1 10 2 11
    2 3 1
    2 4 1
    1 3 8
    2 2 3
    2 1 9
    

    Output

    1
    1
    3
    4

 思路:

本题有两个操作,单点更新就不用说了很简单,重点就在于如何查询。强行暴力复杂度就到了O(n^2),这是肯定不行的,考虑用线段树来维护某个区间相邻两个数差值的最大值,注意是相邻两个数,例如序列1, 5, 4, 9,其最大值就是5(也就是9和4的差)。

至于具体要怎样维护呢,毕竟单点的话是没有差值的。因为所有的差值中0最小(没有负数),我们可以把单点的差值定义为0(此举并没有什么深刻含义,当然你也可以定义为负数,不影响的)。如何定义单点的差值解决了,那么如何将线段树中的小区间合并成大区间呢?

void push_up(int rt)
{
    tree[rt].def = max(tree[rt << 1].def, max(tree[rt << 1 | 1].def, xabs(tree[rt << 1].rc - tree[rt << 1 | 1].lc)));
    tree[rt].lc = tree[rt << 1].lc;
    tree[rt].rc = tree[rt << 1 | 1].rc;
}

这样就OK了,每次比较左区间和右区间差值的最大值和左区间最右端的数与右区间最左端的数的差值。自然线段树里面还要维护当前区间最左端和最右端的元素。

维护差值的线段树就到此结束了,下来就是查询了。

我们先查询所询问点的右端,也就是这个操作:

int l = a, r = n;                                       /// 区间左端点为当前要查询的点,右端点为n
    while(l < r - 1)                                      ///注意,当区间端点值相差为1就可以结束循环了
    {
        int mid = (l + r) / 2;                                                       ///我们的目标是查询出符合条件或者不符合条件的边界位置
        if(query(l, mid, 1, n, 1) > val)                                     ///如果当前区间[ l,  mid]符合条件就往后查询,否则就往前查询
        {
            r = mid;
        }
        else
        {
            l = mid;
        }
    }
    if(xabs(value[r] - value[l]) <= val)                           ///在判断一下r和l哪一个是边界
    {
        res += r - a + 1;
    }
    else
    {
        res += l - a + 1;
    }

同理,查询左区间也是这样子

    l = 1;
    r = a;
    while(l < r - 1)
    {
        int mid = (l + r) / 2;
        if(query(mid, r, 1, n, 1) > val)                              ///注意,这里有区别,要查询的是[mid,  r]区间
        {
            l = mid;                                                             ///还有这了也不一样了,自己模拟模拟,体会一下
        }
        else
        {
            r = mid;
        }
    }
    if(xabs(value[r] - value[l]) <= val)
    {
        res += a - l;
    }
    else
    {
        res += a - l - 1;
    }

 

#include 
#include 
#include 
#include 
#include 

using namespace std;

const int maxn = 100020;
struct Tree
{
    int def;
    int lc;
    int rc;
}tree[maxn << 2];

int value[maxn];
int n, m;

int xabs(int a)
{
    return a > 0 ? a : -a;
}

void push_up(int rt)
{
    tree[rt].def = max(tree[rt << 1].def, max(tree[rt << 1 | 1].def, xabs(tree[rt << 1].rc - tree[rt << 1 | 1].lc)));
    tree[rt].lc = tree[rt << 1].lc;
    tree[rt].rc = tree[rt << 1 | 1].rc;
}

void Build(int l, int r, int rt)
{
    if(l == r)
    {
        tree[rt].lc = tree[rt].rc = value[l];
        tree[rt].def = 0;
        return ;
    }
    int m = (l + r) / 2;
    Build(l, m, rt << 1);
    Build(m + 1, r, rt << 1 | 1);
    push_up(rt);
}

void update(int pos, int val, int l, int r, int rt)
{
    if(l == r && pos == l)
    {
        tree[rt].lc = tree[rt].rc = val;
        return ;
    }
    int m = (l + r) / 2;
    if(m >= pos)
        update(pos, val, l, m, rt << 1);
    else
        update(pos, val, m + 1, r, rt << 1 | 1);
    push_up(rt);
}

int query(int L, int R, int l, int r, int rt)
{
    if(l >= L && r <= R)
        return tree[rt].def;
    int m = (l + r) / 2;
    int ans;
    if(m >= R)
        ans = query(L, R, l, m, rt << 1);
    else if(m < L)
        ans = query(L, R, m + 1, r, rt << 1 | 1);
    else
    {
        ans = max(query(L, R, l, m, rt << 1), max(query(L, R, m + 1, r, rt << 1 | 1), xabs(tree[rt << 1].rc - tree[rt << 1 | 1].lc)));
    }
    return ans;
}

int getans(int a, int val)
{
    int res = 0;
    int l = a, r = n;
    while(l < r - 1)
    {
        int mid = (l + r) / 2;
        if(query(l, mid, 1, n, 1) > val)
        {
            r = mid;
        }
        else
        {
            l = mid;
        }
    }
    if(xabs(value[r] - value[l]) <= val)
    {
        res += r - a + 1;
    }
    else
    {
        res += l - a + 1;
    }
    l = 1;
    r = a;
    while(l < r - 1)
    {
        int mid = (l + r) / 2;
        if(query(mid, r, 1, n, 1) > val)
        {
            l = mid;
        }
        else
        {
            r = mid;
        }
    }
    if(xabs(value[r] - value[l]) <= val)
    {
        res += a - l;
    }
    else
    {
        res += a - l - 1;
    }
    return res;
}

void check(int l, int r, int rt)
{
        cout << l << ' ' << r << ' ' << tree[rt].def << endl;
    if(l == r)
    {
        return ;
    }
    int m = (l + r) / 2;
    check(l, m, rt << 1);
    check(m + 1, r, rt << 1 | 1);
}

int main()
{
   //freopen("in.txt", "r", stdin);
   cin >> n >> m;
   for(int i = 1; i <= n; ++ i)
   {
        scanf("%d", &value[i]);
   }

   Build(1, n, 1);
   //check(1, n, 1);
   int a, b, c;
   for(int i = 1; i <= m; ++ i)
   {
        scanf("%d%d%d", &a, &b, &c);
        if(a == 1)
        {
            update(b, c, 1, n, 1);
            value[b] = c;
            //check(1, n, 1);
        }
        else
        {
            cout << getans(b, c) << endl;
        }
   }
    return 0;
}

 

你可能感兴趣的:(线段树)