牛客西安邮大校赛-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

1
2
3
4
5
6
7
5 5
1 2 3 4 3
q 3
c 1 3 3
q 2
c 5 5 4
q 5

Output

1
2
3
1
2
0

思路

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

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#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;
}