区间最值

区间最值操作指,将区间[l,r][l,r]的数全部对x取max或min,例如ai=max(ai,x)a_i=max(a_i,x)

HDU5306 Gorgeous Sequence

这里比较玄学的是,维护一个最大值和次大值即可,这样的复杂度就能降为logn。这里可以用势能分析法得到证明。

这里使用到的势能分析法在splay的复杂度证明中也用到了,但是没有系统学习,过段时间来填坑。

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
#include <algorithm>
#include <cctype>
#include <cstdio>
using namespace std;
const int N = 1e6 + 6;

char nc() {
static char buf[1000000], *p = buf, *q = buf;
return p == q && (q = (p = buf) + fread(buf, 1, 1000000, stdin), p == q)
? EOF
: *p++;
}
int rd() {
int res = 0;
char c = nc();
while (!isdigit(c)) c = nc();
while (isdigit(c)) res = res * 10 + c - '0', c = nc();
return res;
}

int t, n, m;
int a[N];
int mx[N << 2], se[N << 2], cn[N << 2], tag[N << 2];
long long sum[N << 2];
inline void pushup(int u) { // 向上更新标记
const int ls = u << 1, rs = u << 1 | 1;
sum[u] = sum[ls] + sum[rs];
if (mx[ls] == mx[rs]) {
mx[u] = mx[rs];
se[u] = max(se[ls], se[rs]);
cn[u] = cn[ls] + cn[rs];
} else if (mx[ls] > mx[rs]) {
mx[u] = mx[ls];
se[u] = max(se[ls], mx[rs]);
cn[u] = cn[ls];
} else {
mx[u] = mx[rs];
se[u] = max(mx[ls], se[rs]);
cn[u] = cn[rs];
}
}
inline void pushtag(int u, int tg) { // 单纯地打标记,不暴搜
if (mx[u] <= tg) return;
sum[u] += (1ll * tg - mx[u]) * cn[u];
mx[u] = tag[u] = tg;
}
inline void pushdown(int u) { //下传标记
if (tag[u] == -1) return;
pushtag(u << 1, tag[u]), pushtag(u << 1 | 1, tag[u]);
tag[u] = -1;
}
void build(int u = 1, int l = 1, int r = n) { //建树
tag[u] = -1;
if (l == r) {
sum[u] = mx[u] = a[l], se[u] = -1, cn[u] = 1;
return;
}
int mid = (l + r) >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void modify_min(int L, int R, int v, int u = 1, int l = 1, int r = n) {
if (mx[u] <= v) return;
if (L <= l && r <= R && se[u] < v) return pushtag(u, v);
int mid = (l + r) >> 1;
pushdown(u);
if (L <= mid) modify_min(L, R, v, u << 1, l, mid);
if (mid < R) modify_min(L, R, v, u << 1 | 1, mid + 1, r);
pushup(u);
}
int query_max(int L, int R, int u = 1, int l = 1, int r = n) { //查询最值
if (L <= l && r <= R) return mx[u];
int mid = (l + r) >> 1, r1 = -1, r2 = -1;
pushdown(u);
if (L <= mid) r1 = query_max(L, R, u << 1, l, mid);
if (mid < R) r2 = query_max(L, R, u << 1 | 1, mid + 1, r);
return max(r1, r2);
}
long long query_sum(int L, int R, int u = 1, int l = 1, int r = n) { //数值
if (L <= l && r <= R) return sum[u];
int mid = (l + r) >> 1;
long long res = 0;
pushdown(u);
if (L <= mid) res += query_sum(L, R, u << 1, l, mid);
if (mid < R) res += query_sum(L, R, u << 1 | 1, mid + 1, r);
return res;
}
void go() { //根据题意
n = rd(), m = rd();
for (int i = 1; i <= n; i++) a[i] = rd();
build();
for (int i = 1; i <= m; i++) {
int op, x, y, z;
op = rd(), x = rd(), y = rd();
if (op == 0)
z = rd(), modify_min(x, y, z);
else if (op == 1)
printf("%d\n", query_max(x, y));
else
printf("%lld\n", query_sum(x, y));
}
}
signed main() {
t = rd();
while (t--) go();
return 0;
}

BZOJ4695 最假女选手

类似于上面的方法,记录一个次大/小值。

这里还涉及标记下传的问题,我们可以打一个额外的标记解决此问题。

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
154
155
156
157
158
159
160
161
162
163
164
165
#include <cstdio>
#include <iostream>
using namespace std;

int inline rd() {
register char act = 0;
register int f = 1, x = 0;
while (act = getchar(), act < '0' && act != '-')
;
if (act == '-') f = -1, act = getchar();
x = act - '0';
while (act = getchar(), act >= '0') x = x * 10 + act - '0';
return x * f;
}

const int N = 5e5 + 5, SZ = N << 2, INF = 0x7fffffff;

int n, m;
int a[N];

struct data {
int mx, mx2, mn, mn2, cmx, cmn, tmx, tmn, tad;
long long sum;
} t[SZ];

void pushup(int u) {
const int lu = u << 1, ru = u << 1 | 1;
t[u].sum = t[lu].sum + t[ru].sum;
if (t[lu].mx == t[ru].mx) {
t[u].mx = t[lu].mx, t[u].cmx = t[lu].cmx + t[ru].cmx;
t[u].mx2 = max(t[lu].mx2, t[ru].mx2);
} else if (t[lu].mx > t[ru].mx) {
t[u].mx = t[lu].mx, t[u].cmx = t[lu].cmx;
t[u].mx2 = max(t[lu].mx2, t[ru].mx);
} else {
t[u].mx = t[ru].mx, t[u].cmx = t[ru].cmx;
t[u].mx2 = max(t[lu].mx, t[ru].mx2);
}
if (t[lu].mn == t[ru].mn) {
t[u].mn = t[lu].mn, t[u].cmn = t[lu].cmn + t[ru].cmn;
t[u].mn2 = min(t[lu].mn2, t[ru].mn2);
} else if (t[lu].mn < t[ru].mn) {
t[u].mn = t[lu].mn, t[u].cmn = t[lu].cmn;
t[u].mn2 = min(t[lu].mn2, t[ru].mn);
} else {
t[u].mn = t[ru].mn, t[u].cmn = t[ru].cmn;
t[u].mn2 = min(t[lu].mn, t[ru].mn2);
}
}
void push_add(int u, int l, int r, int v) {
// 更新加法标记的同时,更新 $\min$ 和 $\max$ 标记
t[u].sum += (r - l + 1ll) * v;
t[u].mx += v, t[u].mn += v;
if (t[u].mx2 != -INF) t[u].mx2 += v;
if (t[u].mn2 != INF) t[u].mn2 += v;
if (t[u].tmx != -INF) t[u].tmx += v;
if (t[u].tmn != INF) t[u].tmn += v;
t[u].tad += v;
}
void push_min(int u, int tg) {
// 注意比较 $\max$ 标记
if (t[u].mx <= tg) return;
t[u].sum += (tg * 1ll - t[u].mx) * t[u].cmx;
if (t[u].mn2 == t[u].mx) t[u].mn2 = tg; // !!!
if (t[u].mn == t[u].mx) t[u].mn = tg; // !!!!!
if (t[u].tmx > tg) t[u].tmx = tg; // 更新取 $\max$ 标记
t[u].mx = tg, t[u].tmn = tg;
}
void push_max(int u, int tg) {
if (t[u].mn > tg) return;
t[u].sum += (tg * 1ll - t[u].mn) * t[u].cmn;
if (t[u].mx2 == t[u].mn) t[u].mx2 = tg;
if (t[u].mx == t[u].mn) t[u].mx = tg;
if (t[u].tmn < tg) t[u].tmn = tg;
t[u].mn = tg, t[u].tmx = tg;
}
void pushdown(int u, int l, int r) {
const int lu = u << 1, ru = u << 1 | 1, mid = (l + r) >> 1;
if (t[u].tad)
push_add(lu, l, mid, t[u].tad), push_add(ru, mid + 1, r, t[u].tad);
if (t[u].tmx != -INF) push_max(lu, t[u].tmx), push_max(ru, t[u].tmx);
if (t[u].tmn != INF) push_min(lu, t[u].tmn), push_min(ru, t[u].tmn);
t[u].tad = 0, t[u].tmx = -INF, t[u].tmn = INF;
}
void build(int u = 1, int l = 1, int r = n) {
t[u].tmn = INF, t[u].tmx = -INF; // 取极限
if (l == r) {
t[u].sum = t[u].mx = t[u].mn = a[l];
t[u].mx2 = -INF, t[u].mn2 = INF;
t[u].cmx = t[u].cmn = 1;
return;
}
int mid = (l + r) >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void add(int L, int R, int v, int u = 1, int l = 1, int r = n) {
if (R < l || r < L) return;
if (L <= l && r <= R) return push_add(u, l, r, v); // !!! 忘 return
int mid = (l + r) >> 1;
pushdown(u, l, r);
add(L, R, v, u << 1, l, mid), add(L, R, v, u << 1 | 1, mid + 1, r);
pushup(u);
}
void tomin(int L, int R, int v, int u = 1, int l = 1, int r = n) {
if (R < l || r < L || t[u].mx <= v) return;
if (L <= l && r <= R && t[u].mx2 < v) return push_min(u, v); // BUG: 忘了返回
int mid = (l + r) >> 1;
pushdown(u, l, r);
tomin(L, R, v, u << 1, l, mid), tomin(L, R, v, u << 1 | 1, mid + 1, r);
pushup(u);
}
void tomax(int L, int R, int v, int u = 1, int l = 1, int r = n) {
if (R < l || r < L || t[u].mn >= v) return;
if (L <= l && r <= R && t[u].mn2 > v) return push_max(u, v);
int mid = (l + r) >> 1;
pushdown(u, l, r);
tomax(L, R, v, u << 1, l, mid), tomax(L, R, v, u << 1 | 1, mid + 1, r);
pushup(u);
}
long long qsum(int L, int R, int u = 1, int l = 1, int r = n) {
if (R < l || r < L) return 0;
if (L <= l && r <= R) return t[u].sum;
int mid = (l + r) >> 1;
pushdown(u, l, r);
return qsum(L, R, u << 1, l, mid) + qsum(L, R, u << 1 | 1, mid + 1, r);
}
long long qmax(int L, int R, int u = 1, int l = 1, int r = n) {
if (R < l || r < L) return -INF;
if (L <= l && r <= R) return t[u].mx;
int mid = (l + r) >> 1;
pushdown(u, l, r);
return max(qmax(L, R, u << 1, l, mid), qmax(L, R, u << 1 | 1, mid + 1, r));
}
long long qmin(int L, int R, int u = 1, int l = 1, int r = n) {
if (R < l || r < L) return INF;
if (L <= l && r <= R) return t[u].mn;
int mid = (l + r) >> 1;
pushdown(u, l, r);
return min(qmin(L, R, u << 1, l, mid), qmin(L, R, u << 1 | 1, mid + 1, r));
}
int main() {
n = rd();
for (int i = 1; i <= n; i++) a[i] = rd();
build();
m = rd();
for (int i = 1; i <= m; i++) {
int op, l, r, x;
op = rd(), l = rd(), r = rd();
if (op <= 3) x = rd(); // scanf("%d",&x);
if (op == 1)
add(l, r, x);
else if (op == 2)
tomax(l, r, x);
else if (op == 3)
tomin(l, r, x);
else if (op == 4)
printf("%lld\n", qsum(l, r));
else if (op == 5)
printf("%lld\n", qmax(l, r));
else
printf("%lld\n", qmin(l, r));
}
return 0;
}

小结

本质上处理区间最值的基本思想就是数集信息的分类维护与高效合并

历史最值问题

  • 历史最大/小值
    简单地说,一个位置的历史最大值就是当前位置下曾经出现过的数的最大值。形式化地定义,我们定义一个辅助数组 B,一开始与A完全相同。在A的每次操作后,我们对整个数组max,这样即可把B称为历史最大值。
    历史最小值同理。
  • 历史版本和
    辅助数组B一开始全部是0。在每一次操作后,我们把整个A数组累加到B数组上。B即为历史版本和

这里以生存周期为限进行更新

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

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
char nc() {
static char buf[1000000], *p = buf, *q = buf;
return p == q && (q = (p = buf) + fread(buf, 1, 1000000, stdin), p == q)
? EOF
: *p++;
}
inline ll rd() { // LLONG_MIN LMAX=9,223,372,036,854,775,807
ll s = 0, w = 1;
char ch = nc();
while (ch < '0' || ch > '9') {
if (ch == '-') w = -1;
ch = nc();
}
while (ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = nc();
return s * w;
}
const int N = 1e5 + 7;
struct Tree {
int mx, _mx; // 区间最大值 区间历史最大值
int ad, _ad; // 区间加标记 区间阶段历史最大加标记
int st, _st; // 区间修改值 区间阶段历史最大修改标记
} g[N * 4];
int a[N];
int n, m;
#define ls u * 2
#define rs u * 2 + 1
#define mid (l + r) / 2
inline void pushup(int u) {
g[u].mx = max(g[ls].mx, g[rs].mx);
g[u]._mx = max(g[ls]._mx, g[rs]._mx);
}
inline void pushadd(int u, int v, int _v) {
g[u]._mx = max(g[u]._mx, g[u].mx + _v), g[u].mx += v;
if (g[u].st == INT_MIN)
g[u]._ad = max(g[u]._ad, g[u].ad + _v), g[u].ad += v;
else
g[u]._st = max(g[u]._st, g[u].st + _v), g[u].st += v;
}
inline void pushset(int u, int v, int _v) {
g[u]._mx = max(g[u]._mx, _v), g[u].mx = v;
g[u]._st = max(g[u]._st, _v), g[u].st = v;
}
inline void pushdown(int u, int l, int r) {
if (g[u].ad || g[u]._ad)
pushadd(ls, g[u].ad, g[u]._ad), pushadd(rs, g[u].ad, g[u]._ad),
g[u].ad = g[u]._ad = 0;
if (g[u].st != INT_MIN || g[u]._st != INT_MIN)
pushset(ls, g[u].st, g[u]._st), pushset(rs, g[u].st, g[u]._st),
g[u].st = g[u]._st = INT_MIN;
}
void build(int u = 1, int l = 1, int r = n) {
g[u]._st = g[u].st = INT_MIN;
if (l == r) {
g[u].mx = a[l];
g[u]._mx = a[l];
return;
}
build(ls, l, mid), build(rs, mid + 1, r);
pushup(u);
}
int L, R;
void add(int v, int u = 1, int l = 1, int r = n) {
if (R < l || r < L) return;
if (L <= l && r <= R) return pushadd(u, v, max(v, 0));
pushdown(u, l, r);
add(v, ls, l, mid), add(v, rs, mid + 1, r);
pushup(u);
}
void tset(int v, int u = 1, int l = 1, int r = n) {
if (R < l || r < L) return;
if (L <= l && r <= R) return pushset(u, v, v);
pushdown(u, l, r);
tset(v, ls, l, mid), tset(v, rs, mid + 1, r);
pushup(u);
}
int qmax(int u = 1, int l = 1, int r = n) {
if (R < l || r < L) return INT_MIN;
if (L <= l && r <= R) return g[u].mx;
pushdown(u, l, r);
return max(qmax(ls, l, mid), qmax(rs, mid + 1, r));
}
int qmaxh(int u = 1, int l = 1, int r = n) {
if (R < l || r < L) return INT_MIN;
if (L <= l && r <= R) return g[u]._mx;
pushdown(u, l, r);
return max(qmaxh(ls, l, mid), qmaxh(rs, mid + 1, r));
}
int main() {
n = rd();
for (int i = 1; i <= n; ++i) a[i] = rd();
build();
int m = rd(), z;
for (int i = 1; i <= m; ++i) {
char op = nc();
while (op == ' ' || op == '\r' || op == '\n') op = nc();
L = rd(), R = rd();
if (op == 'Q')
printf("%d\n", qmax());
else if (op == 'A')
printf("%d\n", qmaxh());
else if (op == 'P')
add(rd());
else
tset(rd());
}
return 0;
}