【LOJ3325】「SNOI2020」区间和

题目链接

点击打开链接

题目解法

由于修改操作是区间 chkmax ,不难想到用 SegmentTreeBeats 来维护序列。
接下来,我们便只需要考虑如何处理对区间最小值的区间加操作了。

参考最大子段和的传统线段树解法,同样考虑维护左 L L L 、中 M M M 、右 R R R 、和 S S S 四项数据。
在这四项数据中,我们不仅要计入最优解的数值 s u m sum sum ,同时还要记录最优解包含的区间最小值的个数 M i n c n t Mincnt Mincnt

在一个区间的区间最小值不断增加的过程中,有一些包含更多区间最小值的区间有可能逐渐变得优于原有的最优解。因此,有时我们不得不递归入一些节点的子树,重新计算最优解。
由此,考虑维护 M i n t i m e r Mintimer Mintimer 表示区间最小值至少要增长到何值时,才要递归入子树中重新计算最优解。 M i n t i m e r Mintimer Mintimer 可由子树的 M i n t i m e r Mintimer Mintimer ,以及合并两个子树的 L , M , R , S L,M,R,S L,M,R,S 的过程算得。

由此,我们可以支持修改和询问,得到了一个正确的算法。

考虑分析其时间复杂度。
每当一个节点被递归进入子树,其子树内的至少一个点的 L , M , R , S L,M,R,S L,M,R,S 中的至少一者的 M i n c n t Mincnt Mincnt 将会至少增加 1 1 1 ,因此,向下 DFS 的总次数应当不超过线段树上的区间总长,也即 O ( N L o g N ) O(NLogN) O(NLogN) 。每次 DFS 至多花费 O ( L o g N ) O(LogN) O(LogN) 的时间。

更新:经过和 djq 的讨论,上面的复杂度分析存在一定问题
考虑用两种方式分析 L , R L,R L,R M M M 对复杂度的贡献。
对于 L , R L,R L,R ,注意到一个节点的 L , R L,R L,R 对应的区间长度是单调不降的, L , R L,R L,R 被修改的总次数是 O ( N L o g N ) O(NLogN) O(NLogN) 的。对于 M M M ,考虑对每个节点定义势能函数 f i f_i fi ,表示当前的 M M M 是三个可选的 M M M (左子树,右子树,拼接)中 M i n c n t Mincnt Mincnt 排名第 1 / 2 / 3 1/2/3 1/2/3 大的,则 L , R L,R L,R 的势能每减小 1 1 1 ,只有对应节点祖先的势能可能增加。

时间复杂度 O ( N L o g 2 N + Q L o g N ) O(NLog^2N+QLogN) O(NLog2N+QLogN)

#include
using namespace std;
const int MAXN = 2e5 + 5;
const int INF  = 1e9 + 7;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
struct info {
	int Min, Mincnt; ll sum;
	int count(int x) {
		if (Min == x) return Mincnt;
		else return 0;
	}
	void check(int x) {
		Mincnt = count(x);
	}
	void inc(int x) {
		if (Mincnt != 0) {
			sum += 1ll * x * Mincnt;
			Min += x;
		}
	}
};
info operator + (info a, info b) {
	info res = {0, 0, 0};
	res.sum = a.sum + b.sum;
	res.Min = min(a.Min, b.Min);
	if (res.Min == a.Min) res.Mincnt += a.Mincnt;
	if (res.Min == b.Min) res.Mincnt += b.Mincnt;
	return res;
}
struct subseg {ll LMax, RMax, res, sum; };
subseg operator + (subseg a, subseg b) {
	subseg ans;
	ans.sum = a.sum + b.sum;
	ans.LMax = max(a.LMax, a.sum + b.LMax);
	ans.RMax = max(b.RMax, a.RMax + b.sum);
	ans.res = max(a.RMax + b.LMax, max(a.res, b.res));
	return ans;
}
struct SegmentTree {
	struct Node {
		int lc, rc, add, timer, Mintimer;
		info S, L, R, M; pair <int, int> Min;
	} a[MAXN * 2];
	int n, size, root;
	void update(int root) {
		int lc = a[root].lc, rc = a[root].rc;
		// a[root].Min, a[root].S
		a[root].S = a[lc].S + a[rc].S;
		a[root].Min.first = min(a[lc].Min.first, a[rc].Min.first);
		a[root].Min.second = min(a[lc].Min.second, a[rc].Min.second);
		if (a[lc].Min.first < a[rc].Min.first) chkmin(a[root].Min.second, a[rc].Min.first);
		if (a[rc].Min.first < a[lc].Min.first) chkmin(a[root].Min.second, a[lc].Min.first);
		int Min = a[root].Min.first, timer = INF;
		// a[root].L
		if (a[lc].L.sum > a[lc].S.sum + a[rc].L.sum) {
			a[root].L = a[lc].L;
			a[root].L.check(Min);
			int d = a[lc].S.count(Min) + a[rc].L.count(Min) - a[lc].L.count(Min);
			if (d > 0) timer = min(timer + 0ll, (a[lc].L.sum - (a[lc].S.sum + a[rc].L.sum)) / d + 1);
		} else {
			a[root].L = a[lc].S + a[rc].L;
			a[root].L.check(Min);
		}
		// a[root].R
		if (a[rc].R.sum > a[rc].S.sum + a[lc].R.sum) {
			a[root].R = a[rc].R;
			a[root].R.check(Min);
			int d = a[rc].S.count(Min) + a[lc].R.count(Min) - a[rc].R.count(Min);
			if (d > 0) timer = min(timer + 0ll, (a[rc].R.sum - (a[rc].S.sum + a[lc].R.sum)) / d + 1);
		} else {
			a[root].R = a[rc].S + a[lc].R;
			a[root].R.check(Min);
		}
		// a[root].M
		if (a[lc].R.sum + a[rc].L.sum > a[lc].M.sum && a[lc].R.sum + a[rc].L.sum > a[rc].M.sum) {
			a[root].M = a[lc].R + a[rc].L;
			a[root].M.check(Min);
			int d = a[lc].M.count(Min) - a[lc].R.count(Min) - a[rc].L.count(Min);
			if (d > 0) timer = min(timer + 0ll, (a[lc].R.sum + a[rc].L.sum - a[lc].M.sum) / d + 1);
			d = a[rc].M.count(Min) - a[lc].R.count(Min) - a[rc].L.count(Min);
			if (d > 0) timer = min(timer + 0ll, (a[lc].R.sum + a[rc].L.sum - a[rc].M.sum) / d + 1);
		} else if (a[lc].M.sum > a[rc].M.sum) {
			a[root].M = a[lc].M;
			a[root].M.check(Min);
			int d = a[rc].M.count(Min) - a[lc].M.count(Min);
			if (d > 0) timer = min(timer + 0ll, (a[lc].M.sum - a[rc].M.sum) / d + 1);
			d = a[lc].R.count(Min) + a[rc].L.count(Min) - a[lc].M.count(Min);
			if (d > 0) timer = min(timer + 0ll, (a[lc].M.sum - (a[lc].R.sum + a[rc].L.sum)) / d + 1);
		} else {
			a[root].M = a[rc].M;
			a[root].M.check(Min);
			int d = a[lc].M.count(Min) - a[rc].M.count(Min);
			if (d > 0) timer = min(timer + 0ll, (a[rc].M.sum - a[lc].M.sum) / d + 1);
			d = a[lc].R.count(Min) + a[rc].L.count(Min) - a[rc].M.count(Min);
			if (d > 0) timer = min(timer + 0ll, (a[rc].M.sum - (a[lc].R.sum + a[rc].L.sum)) / d + 1);
		}
		// a[root].timer
		a[root].timer = min(a[root].Min.second, timer + Min);
		a[root].Mintimer = min(a[root].timer, min(a[lc].Mintimer, a[rc].Mintimer));
	}
	void build(int &root, int l, int r, int *v) {
		root = ++size;
		if (l == r) {
			a[root].Min = make_pair(v[l], INF);
			a[root].timer = a[root].Mintimer = INF;
			a[root].S = a[root].L = a[root].R = a[root].M = {v[l], 1, v[l]};
			return;
		}
		int mid = (l + r) / 2;
		build(a[root].lc, l, mid, v);
		build(a[root].rc, mid + 1, r, v);
		update(root);
	}
	void init(int x, int *v) {
		n = x, size = 0;
		build(root, 1, n, v);
	}
	void addtag(int root, int d) {
		a[root].add += d;
		a[root].S.inc(d);
		a[root].L.inc(d);
		a[root].R.inc(d);
		a[root].M.inc(d);
		a[root].Min.first += d;
		assert(a[root].Min.first < a[root].Min.second);
	}
	void pushdown(int root) {
		if (a[root].add) {
			int lc = a[root].lc, rc = a[root].rc, Min = min(a[lc].Min.first, a[rc].Min.first);
			if (a[lc].Min.first == Min) addtag(lc, a[root].add);
			if (a[rc].Min.first == Min) addtag(rc, a[root].add);
			a[root].add = 0;
		}
	}
	void chkmax(int root, int l, int r, int ql, int qr, int x) {
		if (x <= a[root].Min.first) return;
		if (l == ql && r == qr) {
			if (x < a[root].Mintimer) {
				addtag(root, x - a[root].Min.first);
				return;
			}
			pushdown(root);
			int mid = (l + r) / 2;
			chkmax(a[root].lc, l, mid, l, mid, x);
			chkmax(a[root].rc, mid + 1, r, mid + 1, r, x);
			update(root);
			return;
		}
		pushdown(root);
		int mid = (l + r) / 2;
		if (mid >= ql) chkmax(a[root].lc, l, mid, ql, min(mid, qr), x);
		if (mid + 1 <= qr) chkmax(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, x);
		update(root);
	}
	void chkmax(int l, int r, int x) {
		chkmax(root, 1, n, l, r, x);
	}
	subseg query(int root, int l, int r, int ql, int qr) {
		if (l == ql && r == qr) return (subseg) {a[root].L.sum, a[root].R.sum, a[root].M.sum, a[root].S.sum};
		pushdown(root);
		int mid = (l + r) / 2;
		if (mid >= qr) return query(a[root].lc, l, mid, ql, qr);
		else if (mid + 1 <= ql) return query(a[root].rc, mid + 1, r, ql, qr);
		else return query(a[root].lc, l, mid, ql, mid) + query(a[root].rc, mid + 1, r, mid + 1, qr);
	}
	ll query(int l, int r) {
		return max(0ll, query(root, 1, n, l, r).res);
	}
} ST;
int a[MAXN];
int main() {
	int n, q; read(n), read(q);
	for (int i = 1; i <= n; i++)
		read(a[i]);
	ST.init(n, a);
	for (int i = 1; i <= q; i++) {
		int opt, l, r; read(opt), read(l), read(r);
		if (opt == 0) {
			int x; read(x);
			ST.chkmax(l, r, x);
		} else printf("%lld\n", ST.query(l, r));
	}
	return 0;
}

你可能感兴趣的:(【OJ】LOJ)