区间历史最值线段树记录
区间+/chkmin/求最值/求历史最值
Description
维护一个线段树,使得可以实现区间加、区间 chkmin、求区间和、区间历史最大值、区间历史最大值。
Solution
先不考虑区间 chkmin 和历史最值,可以直接对于每个线段树节点维护一个 tag,每次 addtag 更新。
加上区间历史最值后,先考虑对于单个线段树节点怎么更新。
容易发现对于一个节点,在它下传标记之前一定就是很多次整体加某个数,并且儿子的 \(sum\) 和 \(max\) 数组是不会变的。
考虑维护 \(tag2[x]\) 表示 \(x\) 这个节点上次下传标记到当前时刻的最大前缀和,那么每次下传标记时先让 \(maxb[son]\leftarrow maxa[son]+tag2[x]\) 再更新 \(maxa[son]\) 就可以维护区间历史最值了。
加上区间 chkmin 就用吉司机线段树的技巧维护区间最大值、次大值,并且由于这里最大值和非最大值有些操作是分开的,所以需要对于最大、非最大值分别维护 tag。
时间复杂度:\(O(n\log^2 n)\)。
Code
Code
#include <bits/stdc++.h>
// #define int int64_t
const int kMaxN = 5e5 + 5;
int n, m;
int a[kMaxN];
struct SGT {
int maxa[kMaxN * 4], maxb[kMaxN * 4];
int cnt[kMaxN * 4], se[kMaxN * 4];
int tag1[kMaxN * 4], tag2[kMaxN * 4], tag3[kMaxN * 4], tag4[kMaxN * 4];
int64_t sum[kMaxN * 4];
void pushup(int x) {
sum[x] = sum[x << 1] + sum[x << 1 | 1];
maxa[x] = std::max(maxa[x << 1], maxa[x << 1 | 1]);
maxb[x] = std::max(maxb[x << 1], maxb[x << 1 | 1]);
if (maxa[x << 1] == maxa[x << 1 | 1]) {
se[x] = std::max(se[x << 1], se[x << 1 | 1]);
cnt[x] = cnt[x << 1] + cnt[x << 1 | 1];
} else if (maxa[x << 1] > maxa[x << 1 | 1]) {
se[x] = std::max(se[x << 1], maxa[x << 1 | 1]);
cnt[x] = cnt[x << 1];
} else {
se[x] = std::max(maxa[x << 1], se[x << 1 | 1]);
cnt[x] = cnt[x << 1 | 1];
}
}
void addtag(int x, int l, int r, int v1, int v2, int v3, int v4) {
maxb[x] = std::max(maxb[x], maxa[x] + v2);
tag2[x] = std::max(tag2[x], tag1[x] + v2);
tag4[x] = std::max(tag4[x], tag3[x] + v4);
maxa[x] += v1, tag1[x] += v1, tag3[x] += v3;
sum[x] += 1ll * cnt[x] * v1 + 1ll * (r - l + 1 - cnt[x]) * v3;
if (se[x] != -1e18) se[x] += v3;
}
void pushdown(int x, int l, int r) {
if (tag1[x] || tag2[x] || tag3[x] || tag4[x]) {
int mx = std::max(maxa[x << 1], maxa[x << 1 | 1]);
int mid = (l + r) >> 1;
if (maxa[x << 1] == mx) {
addtag(x << 1, l, mid, tag1[x], tag2[x], tag3[x], tag4[x]);
} else {
addtag(x << 1, l, mid, tag3[x], tag4[x], tag3[x], tag4[x]);
}
if (maxa[x << 1 | 1] == mx) {
addtag(x << 1 | 1, mid + 1, r, tag1[x], tag2[x], tag3[x], tag4[x]);
} else {
addtag(x << 1 | 1, mid + 1, r, tag3[x], tag4[x], tag3[x], tag4[x]);
}
tag1[x] = tag2[x] = tag3[x] = tag4[x] = 0;
}
}
void build(int x, int l, int r) {
se[x] = -2e9;
if (l == r) {
sum[x] = maxa[x] = maxb[x] = a[l], cnt[x] = 1;
return;
}
int mid = (l + r) >> 1;
build(x << 1, l, mid), build(x << 1 | 1, mid + 1, r);
pushup(x);
}
void update1(int x, int l, int r, int ql, int qr, int v) { // 区间 +
if (l > qr || r < ql) return;
else if (l >= ql && r <= qr) return addtag(x, l, r, v, std::max<int>(v, 0), v, std::max<int>(v, 0));
pushdown(x, l, r);
int mid = (l + r) >> 1;
update1(x << 1, l, mid, ql, qr, v), update1(x << 1 | 1, mid + 1, r, ql, qr, v);
pushup(x);
}
void update2(int x, int l, int r, int ql, int qr, int v) { // 区间 chkmin
if (l > qr || r < ql || maxa[x] <= v) return;
else if (l >= ql && r <= qr && se[x] < v)
return addtag(x, l, r, v - maxa[x], 0, 0, 0);
pushdown(x, l, r);
int mid = (l + r) >> 1;
update2(x << 1, l, mid, ql, qr, v), update2(x << 1 | 1, mid + 1, r, ql, qr, v);
pushup(x);
}
int64_t querysum(int x, int l, int r, int ql, int qr) {
if (l > qr || r < ql) return 0;
else if (l >= ql && r <= qr) return sum[x];
pushdown(x, l, r);
int mid = (l + r) >> 1;
return querysum(x << 1, l, mid, ql, qr) + querysum(x << 1 | 1, mid + 1, r, ql, qr);
}
int querymaxa(int x, int l, int r, int ql, int qr) {
if (l > qr || r < ql) return -2e9;
else if (l >= ql && r <= qr) return maxa[x];
pushdown(x, l, r);
int mid = (l + r) >> 1;
return std::max(querymaxa(x << 1, l, mid, ql, qr), querymaxa(x << 1 | 1, mid + 1, r, ql, qr));
}
int querymaxb(int x, int l, int r, int ql, int qr) {
if (l > qr || r < ql) return -2e9;
else if (l >= ql && r <= qr) return maxb[x];
pushdown(x, l, r);
int mid = (l + r) >> 1;
return std::max(querymaxb(x << 1, l, mid, ql, qr), querymaxb(x << 1 | 1, mid + 1, r, ql, qr));
}
} sgt;
void dickdreamer() {
std::cin >> n >> m;
for (int i = 1; i <= n; ++i) std::cin >> a[i];
sgt.build(1, 1, n);
for (int i = 1; i <= m; ++i) {
int op, l, r, v;
std::cin >> op >> l >> r;
if (op == 1) {
std::cin >> v;
sgt.update1(1, 1, n, l, r, v);
} else if (op == 2) {
std::cin >> v;
sgt.update2(1, 1, n, l, r, v);
} else if (op == 3) {
std::cout << sgt.querysum(1, 1, n, l, r) << '\n';
} else if (op == 4) {
std::cout << sgt.querymaxa(1, 1, n, l, r) << '\n';
} else {
std::cout << sgt.querymaxb(1, 1, n, l, r) << '\n';
}
}
}
int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}
区间+/赋值/求最值/求历史最值
Description
维护一个线段树,使得可以实现区间加、区间赋值、求区间最值、区间历史最值。
Solution
如果没有区间覆盖就像刚才一样维护两个 tag。但是如果加上区间覆盖后由于 tag 维护的是初始到终止的整体位移量,而只要一个节点被区间赋值后所有数就变得相等,那么这个位移量就没用了。
所以考虑对于每个节点,维护做区间赋值之前的两个 tag,然后维护做区间赋值后的当前赋的值和赋的值的最大值。
时间复杂度:\(O(n\log n)\)。
Code
Code
#include <bits/stdc++.h>
#define int int64_t
const int kMaxN = 1e5 + 5;
int n, m;
int a[kMaxN];
struct SGT {
int maxa[kMaxN * 4], maxb[kMaxN * 4];
int tag1[kMaxN * 4], tag2[kMaxN * 4], tagcov1[kMaxN * 4], tagcov2[kMaxN * 4];
void pushup(int x) {
maxa[x] = std::max(maxa[x << 1], maxa[x << 1 | 1]);
maxb[x] = std::max(maxb[x << 1], maxb[x << 1 | 1]);
}
void addtag1(int x, int v1, int v2) {
maxb[x] = std::max(maxb[x], maxa[x] + v2);
maxa[x] += v1;
if (tagcov1[x] == -1e18) {
tag2[x] = std::max(tag2[x], tag1[x] + v2), tag1[x] += v1;
} else {
tagcov2[x] = std::max(tagcov2[x], tagcov1[x] + v2), tagcov1[x] += v1;
}
}
void addtag2(int x, int v1, int v2) {
maxb[x] = std::max(maxb[x], v2);
maxa[x] = v1;
tagcov2[x] = std::max(tagcov2[x], v2);
tagcov1[x] = v1;
}
void pushdown(int x) {
if (tag1[x] || tag2[x]) {
addtag1(x << 1, tag1[x], tag2[x]);
addtag1(x << 1 | 1, tag1[x], tag2[x]);
tag1[x] = tag2[x] = 0;
}
if (tagcov1[x] != -1e18 || tagcov2[x] != -1e18) {
addtag2(x << 1, tagcov1[x], tagcov2[x]);
addtag2(x << 1 | 1, tagcov1[x], tagcov2[x]);
tagcov1[x] = tagcov2[x] = -1e18;
}
}
void build(int x, int l, int r) {
tagcov1[x] = tagcov2[x] = -1e18;
if (l == r) {
maxa[x] = maxb[x] = a[l];
return;
}
int mid = (l + r) >> 1;
build(x << 1, l, mid), build(x << 1 | 1, mid + 1, r);
pushup(x);
}
void update1(int x, int l, int r, int ql, int qr, int v) {
if (l > qr || r < ql) return;
else if (l >= ql && r <= qr) return addtag1(x, v, std::max<int>(0, v));
pushdown(x);
int mid = (l + r) >> 1;
update1(x << 1, l, mid, ql, qr, v), update1(x << 1 | 1, mid + 1, r, ql, qr, v);
pushup(x);
}
void update2(int x, int l, int r, int ql, int qr, int v) {
if (l > qr || r < ql) return;
else if (l >= ql && r <= qr) return addtag2(x, v, v);
pushdown(x);
int mid = (l + r) >> 1;
update2(x << 1, l, mid, ql, qr, v), update2(x << 1 | 1, mid + 1, r, ql, qr, v);
pushup(x);
}
int querymaxa(int x, int l, int r, int ql, int qr) {
if (l > qr || r < ql) return -1e18;
else if (l >= ql && r <= qr) return maxa[x];
pushdown(x);
int mid = (l + r) >> 1;
return std::max(querymaxa(x << 1, l, mid, ql, qr), querymaxa(x << 1 | 1, mid + 1, r, ql, qr));
}
int querymaxb(int x, int l, int r, int ql, int qr) {
if (l > qr || r < ql) return -1e18;
else if (l >= ql && r <= qr) return maxb[x];
pushdown(x);
int mid = (l + r) >> 1;
return std::max(querymaxb(x << 1, l, mid, ql, qr), querymaxb(x << 1 | 1, mid + 1, r, ql, qr));
}
} sgt;
void dickdreamer() {
std::cin >> n;
for (int i = 1; i <= n; ++i) std::cin >> a[i];
sgt.build(1, 1, n);
std::cin >> m;
for (int i = 1; i <= m; ++i) {
std::string op;
int l, r, v;
std::cin >> op >> l >> r;
if (op[0] == 'Q') {
std::cout << sgt.querymaxa(1, 1, n, l, r) << '\n';
} else if (op[0] == 'A') {
std::cout << sgt.querymaxb(1, 1, n, l, r) << '\n';
} else if (op[0] == 'P') {
std::cin >> v;
sgt.update1(1, 1, n, l, r, v);
} else {
std::cin >> v;
sgt.update2(1, 1, n, l, r, v);
}
}
}
int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}