势能线段树
1.算法分析
本文章目的主要是为了留个区间取max/min的模板,代码参考:https://oi-wiki.org/ds/seg-beats/#hdu5306-gorgeous-sequence
2.典型例题
2.1 区间与t取min/询问区间max
HDU5306 Gorgeous Sequence
题意: 维护一个长度为n(1e5)的序列a,进行如下的1e5次操作:
0 l r t:将[l, r]内的元素与t取min,a[i] = min(a[i], t)
1 l r t:输出[l, r]中的最大值
2 l r:输出[l, r]区间和
**题解: ** 本题取区间min,然后询问最大值;如果我取区间max,询问最小值,只需要在建树的时候全部取负号即可。
代码:
#include <bits/stdc++.h>
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]; // 1e6个数字
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