势能线段树

感觉这个科技挺有用,能解决一些常用而又阴间的操作,所以就写一下(

模板:给你一个长度为 n 序列,m 次操作,有三种操作:

0 l r x: 对于 i[l,r] 执行 ai=min(ai,x)
1 l r:输出 max(al,al+1...ar)
2 l r:输出 i=lrai

1n,m2×105

其实这道题可以不用势能线段树,我一来想的是一个分块的做法:

取块长 n,对于每一块,我们维护原序列这个块内的元素排序后的结果。

修改时散块暴力重构,复杂度 O(n),修改整块时还是暴力重构,从大往小扫到 ait 为止,因为序列只会减小不会增加,这样复杂度 O(n)

总的复杂度 O(mn),稳过校内OJ过于老年估计跑不了

但这个玩意儿但凡我们区间修改加个 max 操作它就 O(mnlogn) 了(整块要二分而不能暴力了),这就不好,所以我们使用势能线段树。

后两个操作是线段树的常规操作,而修改操作对区间和的影响不太好直接计算。

对于线段树每个节点,我们维护 mx,mxcnt,secmx,sum 分别表示最大值,最大值出现次数,次大值,区间和。

每次修改到当前节点,且修改区间完全包含当前节点管辖的区间,则分为三种情况:

  1. mxx,直接不管跑路。
  2. secmx<x,当前节点 sum=mxcnt×(mxx),打个懒标记。
  3. xsecmx,递归进入左右子树继续修改。

那么到此时,我们就得到了一个至少正确性没有问题的算法。

但修改操作的第三种情况,不会拖累全局的复杂度吗?

考虑势能分析。设当前势能为这颗线段树中,每个节点管辖区间中不同的数字个数总和。开始的时候,势能为 O(nlogn)。并且修改操作显然最多让势能 +1

每当我们处于第三种情况,继续递归进入左右子树时,势能必定减少 1。而当势能 =1 时,不论哪个节点必定没有次大值,也就不会出现 xsecmx 的情况。

即复杂度均摊下来是严格 O(nlogn+mlogn) 的。

#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 中国大陆许可协议进行许可。

posted @   zqs2020  阅读(202)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.