势能线段树
感觉这个科技挺有用,能解决一些常用而又阴间的操作,所以就写一下(
模板:给你一个长度为 序列, 次操作,有三种操作:
0 l r x
: 对于 执行 。
1 l r
:输出 。
2 l r
:输出 。
。
其实这道题可以不用势能线段树,我一来想的是一个分块的做法:
取块长 ,对于每一块,我们维护原序列这个块内的元素排序后的结果。
修改时散块暴力重构,复杂度 ,修改整块时还是暴力重构,从大往小扫到 为止,因为序列只会减小不会增加,这样复杂度 。
总的复杂度 ,稳过校内OJ过于老年估计跑不了。
但这个玩意儿但凡我们区间修改加个 操作它就 了(整块要二分而不能暴力了),这就不好,所以我们使用势能线段树。
后两个操作是线段树的常规操作,而修改操作对区间和的影响不太好直接计算。
对于线段树每个节点,我们维护 分别表示最大值,最大值出现次数,次大值,区间和。
每次修改到当前节点,且修改区间完全包含当前节点管辖的区间,则分为三种情况:
- ,直接不管跑路。
- ,当前节点 ,打个懒标记。
- ,递归进入左右子树继续修改。
那么到此时,我们就得到了一个至少正确性没有问题的算法。
但修改操作的第三种情况,不会拖累全局的复杂度吗?
考虑势能分析。设当前势能为这颗线段树中,每个节点管辖区间中不同的数字个数总和。开始的时候,势能为 。并且修改操作显然最多让势能 。
每当我们处于第三种情况,继续递归进入左右子树时,势能必定减少 。而当势能 时,不论哪个节点必定没有次大值,也就不会出现 的情况。
即复杂度均摊下来是严格 的。
#include <cstdio>
#define int long long
inline int max(const int x, const int y) {return x > y ? x : y;}
struct Node {
int l, r, mx, mxcnt, semx, sum, lazy;
} tree[800005];
int a[200005];
inline void pushup(int O) {
int v[] = {tree[O << 1].mx, tree[O << 1].semx, tree[O << 1 | 1].mx, tree[O << 1 | 1].semx};
int c[] = {tree[O << 1].mxcnt, 0, tree[O << 1 | 1].mxcnt, 0};
tree[O].mx = max(v[0], v[2]);
tree[O].mxcnt = 0;
if (v[0] == tree[O].mx) tree[O].mxcnt += c[0], v[0] = 0;
if (v[2] == tree[O].mx) tree[O].mxcnt += c[2], v[2] = 0;
tree[O].semx = max(v[0], max(v[1], max(v[2], v[3])));
tree[O].sum = tree[O << 1].sum + tree[O << 1 | 1].sum;
}
inline void pushdown(int O) {
if (tree[O].lazy < tree[O << 1].mx) {
tree[O << 1].sum -= tree[O << 1].mxcnt * (tree[O << 1].mx - tree[O].lazy);
tree[O << 1].mx = tree[O].lazy;
tree[O << 1].lazy = tree[O].lazy;
}
if (tree[O].lazy < tree[O << 1 | 1].mx) {
tree[O << 1 | 1].sum -= tree[O << 1 | 1].mxcnt * (tree[O << 1 | 1].mx - tree[O].lazy);
tree[O << 1 | 1].mx = tree[O].lazy;
tree[O << 1 | 1].lazy = tree[O].lazy;
}
tree[O].lazy = 1e18;
}
void build(int O, int L, int R) {
tree[O].l = L, tree[O].r = R, tree[O].lazy = 1e18;
if (L != R) {
build(O << 1, L, L + R >> 1);
build(O << 1 | 1, (L + R >> 1) + 1, R);
pushup(O);
} else tree[O].mx = tree[O].sum = a[L], tree[O].mxcnt = 1;
}
void update(int O, int L, int R, int x) {
if (L <= tree[O].l && tree[O].r <= R) {
if (tree[O].mx <= x) return;
if (tree[O].semx < x) {
tree[O].sum -= tree[O].mxcnt * (tree[O].mx - x), tree[O].mx = tree[O].lazy = x;
return;
}
pushdown(O);
update(O << 1, L, R, x);
update(O << 1 | 1, L, R, x);
pushup(O);
return;
}
int mid = tree[O].l + tree[O].r >> 1;
pushdown(O);
if (L <= mid) update(O << 1, L, R, x);
if (mid < R) update(O << 1 | 1, L, R, x);
pushup(O);
}
int getmax(int O, int L, int R) {
if (L <= tree[O].l && tree[O].r <= R) return tree[O].mx;
pushdown(O);
int mid = tree[O].l + tree[O].r >> 1, ans = 0;
if (L <= mid) ans = getmax(O << 1, L, R);
if (mid < R) ans = max(ans, getmax(O << 1 | 1, L, R));
return ans;
}
int getsum(int O, int L, int R) {
if (L <= tree[O].l && tree[O].r <= R) return tree[O].sum;
pushdown(O);
int mid = tree[O].l + tree[O].r >> 1, ans = 0;
if (L <= mid) ans = getsum(O << 1, L, R);
if (mid < R) ans += getsum(O << 1 | 1, L, R);
return ans;
}
signed main() {
int n, m;
scanf("%lld%lld", &n, &m);
for (int i = 1; i <= n; ++ i) scanf("%lld", a + i);
build(1, 1, n);
while (m --) {
int opt, l, r, v;
scanf("%lld%lld%lld", &opt, &l, &r);
if (opt == 0) {scanf("%lld", &v); if (v) update(1, l, r, v);}
else if (opt == 1) printf("%lld\n", getmax(1, l, r));
else printf("%lld\n", getsum(1, l, r));
}
return 0;
}
本文作者:zqs2020
本文链接:https://www.cnblogs.com/stinger/p/15645189.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步