牛客西安邮大校赛-D最简单的一道题


参考自官方的博客:https://blog.nowcoder.net/n/699c671bf3e24676a6df403f68686d73

题目

题面

有一个 n 个数的数组,代表 n 栋建筑,有如下两种操作:

  1. 修改:区间 l 到 r 内的建筑增高 k(正整数)
  2. 查询:对于某个位置 x,求 y 的数目( y \gt xy>x ,且对于位置 y ,位置 x 到位置 y 的这段区间的最小值是位置 y 的值)

 

Input

第一行两个数字 n 和 m , 表示数字个数和操作个数
第二行 n 个数字,表示建筑高度
后面 m 行指令:c l r k
或者:q x
数据范围: 1≤n,m≤2×10^5,1≤l,r≤n
1≤x≤n,1≤k≤10^4

Output

对于每个查询,输出答案,每个答案一行

Examples

Input

5 5
1 2 3 4 3
q 3
c 1 3 3
q 2
c 5 5 4
q 5

Output

1
2
0

思路

思路就是线段树,但是要对val,在l与r区间内能组成下降数列的数不断进行更新(感觉有点像dp了。。。)直接用线段树的话就会超时(还是太菜了)题目中y的数量实际上就是从x开始到最后的递减数列中数的数量(每次遇到小的数就放在后面,不能自己选择),我是看的官方的代码打的注释(把结构体当类用,看起来是真的吃力。。。。还是太菜了)

代码

#include<iostream>
#include<algorithm>
#include<time.h>
using namespace std;

#define mid ((l+r)>>1)

#define MAX 200005
struct node {
    node() {
        minn = value = add = 0;
    }
    void init() {
        minn = value = add = 0;
    }
    int minn;
    int value;// value为在l - r中前半部分的最小值与后半部分区间内能组成下降数列的数(即以前半部分的最小值min为首,在m - r之间取最多的数与min组成递减数列)
    int add;//其子节点的所有数字加add
};

struct Segt {
    int arr[MAX];
    node tree[MAX << 2];
    void push_down(int p) {
        if (tree[p].add != 0) {
            tree[(p << 1)].add += tree[p].add;
            tree[((p << 1) | 1)].add += tree[p].add;
            tree[(p << 1)].minn += tree[p].add;
            tree[((p << 1) | 1)].minn += tree[p].add;
            tree[p].add = 0;
        }

    }

    void push_up(int l, int r, int p) {//向上跟新父节点的值
        //bool flag = true;
        tree[p].minn = min(tree[(p << 1)].minn, tree[((p << 1) | 1)].minn);
        int m = mid;
        int val = tree[(p << 1)].minn;
        tree[p].value = que(m + 1, val, m + 1, r, ((p << 1) | 1));
    }

    void build(int l, int r, int p) {//建立线段树
        tree[p].init();
        if (l == r) {
            tree[p].minn = arr[l];
            tree[p].value = 0;
            return;
        }
        int m = mid;
        build(l, m, (p << 1)); build(m + 1, r, ((p << 1) | 1));
        push_up(l, r, p);
    }
    void update(int a, int b, int k, int l, int r, int p) {//区间修改
        if (l >= a && r <= b) {
            tree[p].minn += k;
            tree[p].add += k;
            return;
        }
        push_down(p);
        int m = mid;
        if (a <= m) update(a, b, k, l, m, (p << 1));
        if (b > m) update(a, b, k, m + 1, r, ((p << 1) | 1));
        push_up(l, r, p);
    }
    int get_val(int pos, int l, int r, int p) {//取得pos点的值(单点查询)
        if (l == r) return tree[p].minn;
        push_down(p);
        int m = mid;
        if (pos <= m) return get_val(pos, l, m, (p << 1));
        else return get_val(pos, m + 1, r, ((p << 1) | 1));
        // push_up(p);
    }
    int que(int a, int& val, int l, int r, int p) {//返回 在a点之前的最小值为val,在l与r区间内能组成下降数列的数(即以val为首,在l-r之间取最多的数与val组成递减数列)
        if (a > r) return 0;//如果a位置在r之后,那么在区间l-r内就没有可以没做条件的数
        int m = mid;
           //cout << l << ' ' << r << endl;
        if (l != r) push_down(p);//更新子节点数值
        if (l >= a) {
            if (tree[p].minn <= val) {//只有后面数值的最小值比val小才能组成
                if (l == r) {
                      //cout << "#" << a<<" "<< val << ' ' <<  l << ' ' << tree[p].minn << endl;
                    val = tree[p].minn;//之后的数要小于val
                    return 1;//minn是一个可以接在val后面的数
                }
                if (tree[(p << 1)].minn <= val) {//如果r-l的前半部分的最小值比val小
                    int an = que(a, val, l, m, (p << 1));//求l-mid与val构成的递减数组
                    val = tree[p].minn;//更新最小值
                     // cout << "@" << val << ' ' << l << ' ' << r << ' ' << an << ' ' << tree[p].value << endl;
                    return an + tree[p].value;//因为value为在l-r中前半部分的最小值与后半部分区间内能组成下降数列的数(即以前半部分的最小值min为首,在m-r之间取最多的数与min组成递减数列)
                }
                else {
                    return que(a, val, m + 1, r, ((p << 1) | 1));//如果前半部分的最小值比val大,则比较后半部分与其可以构成的数的数量
                }
            }
            else {
                return 0;
            }
        }
        //当a不在l之前时
        if (a <= m) {//a在前半部分则搜索前半部分,看a在前半部分可以构成含有多少数的递减数组
            int an = que(a, val, l, m, (p << 1));
            if (val == tree[(p << 1)].minn) {//如果val等于前半部分的最小值,那么说明可以与前半部分进行组合,之后就加上value(前半部分的最小值与后半部分区间内能组成下降数列的数)
                val = tree[p].minn;
                return an + tree[p].value;
            }
            else {
                return an + que(a, val, m + 1, r, ((p << 1) | 1));//不等于的话,即前半部分没有可以配对的数,则an==0,看之后的数与其配对的数量
            }
        }
        else {
            return que(a, val, m + 1, r, ((p << 1) | 1));//如果m在后半部分则搜索后半部分
        }
    }



};
Segt segt;

void make_ans(istream& in, ostream& out) {//输入,输出流
    int n, m;
    in >> n >> m;//等价于 cin>>
    for (int i = 1; i <= n; i++) {
        in >> segt.arr[i];
    }
    segt.build(1, n, 1);
    while (m--) {
        char ch;
          //cout << "------" << endl;
          //for(int i = 1; i <= n; i++) cout << segt.get_val(i,1,n,1) << ' '; cout << endl;
          //cout << "------" << endl;
        in >> ch;
        if (ch == 'c') {
            int l, r, k;
            in >> l >> r >> k;
            segt.update(l, r, k, 1, n, 1);
        }
        else {
            int x;
            in >> x;
            int val = segt.get_val(x, 1, n, 1);//取得x点的值
            //  cout << val << endl;
            out << segt.que(x + 1, val, 1, n, 1) << endl;//输出最小值为val时,在x+1之后有多少数字与其构成递减数列
        }
    }
}

int main(void) {
    freopen("F:/y.txt","r",stdin);
    make_ans(cin, cout);
    return 0;
}

文章作者: xucanxx
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 xucanxx !
  目录