【BZOJ4355】Play with Sequence

【题目链接】

【思路要点】

  • 题目中提到的操作均为区间操作,考虑使用线段树。
  • 注意到数列中的数始终非负,因此3号操作可以看做询问最小值以及其出现次数。
  • 2号操作并非线段树的一般操作,无法直接支持标记合并。
  • 考虑一个被2号操作定位的区间,记区间最小值为\(Min\),严格次小值为\(Mim\),对该区间实行操作数为\(Val\)的2号操作。
  • 若\(Min+Val≥0\),显然这个操作可以看做一个区间加(减)操作。
  • 若\(Min+Val<0\)且\(Mim+Val>0\),我们可以将这个操作看作是对区间中最小值的整体加(减)操作。
  • 若\(Mim+Val≤0\),那么这就意味着最小值和严格次小值将在本次操作后相等,为了重新得到区间严格次小值,我们暴力对该区间在线段树上的两个子节点分别操作一次,并将信息合并。
  • 用势能分析法能够证明,这样的做法存在时间复杂度下界\(O(MLog^{2}N)\),并且实际运行往往表现出优于该复杂度的效率。证明详见国家集训队2016论文集之《区间最值操作与历史最值问题——杭州学军中学 吉如一》。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 300005;
const long long INF = 1e18;
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;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
struct info {long long Min, Mim; int Cnt; };
info operator + (info a, info b) {
	info ans;
	ans.Min = min(a.Min, b.Min);
	ans.Mim = min(a.Mim, b.Mim);
	ans.Cnt = 0;
	if (a.Min != ans.Min) ans.Mim = min(ans.Mim, a.Min);
	else ans.Cnt += a.Cnt;
	if (b.Min != ans.Min) ans.Mim = min(ans.Mim, b.Min);
	else ans.Cnt += b.Cnt;
	return ans;
}
struct SegmentTree {
	struct Node {
		int lc, rc, tag;
		long long val, delta;
		info now;
	} a[MAXN * 2];
	int n, root, tot, value[MAXN];
	void update(int root) {
		a[root].now = a[a[root].lc].now + a[a[root].rc].now;
	}
	void build(int &root, int l, int r) {
		root = ++tot;
		if (l == r) {
			a[root].now = (info) {value[l], INF, 1};
			return;
		}
		int mid = (l + r) / 2;
		build(a[root].lc, l, mid);
		build(a[root].rc, mid + 1, r);
		update(root);
	}
	void init(int x) {
		n = x;
		tot = root = 0;
		build(root, 1, n);
	}
	void pushdown();
	void cover(int root, int l, int r, long long val) {
		a[root].tag = 1;
		a[root].val = val;
		a[root].delta = 0;
		a[root].now = (info) {val, INF, r - l + 1};
	}
	void cover(int root, int l, int r, int ql, int qr, long long val) {
		if (l == ql && r == qr) {
			cover(root, l, r, val);
			return;
		}
		pushdown(root, l, r);
		int mid = (l + r) / 2;
		if (mid >= ql) cover(a[root].lc, l, mid, ql, min(mid, qr), val);
		if (mid + 1 <= qr) cover(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, val);
		update(root);
	}
	void cover(int l, int r, long long val) {
		cover(root, 1, n, l, r, val);
	}
	void add(int root, int l, int r, long long val) {
		if (a[root].tag == 1) {
			a[root].val += val;
			a[root].delta = 0;
			if (a[root].val < 0) a[root].val = 0;
			a[root].now.Min = a[root].val;
		} else {
			if (a[root].tag == 0) {
				a[root].tag = 2;
				a[root].val = 0;
			}
			a[root].val += val;
			a[root].now.Min += val;
			a[root].now.Mim += val;
			if (a[root].now.Min >= 0) return;
			if (a[root].now.Mim > 0) {
				a[root].delta -= a[root].now.Min;
				a[root].now.Min = 0;
			} else {
				pushdown(root, l, r);
				update(root);
			}
		}
	}
	void add(int root, int l, int r, int ql, int qr, long long val) {
		if (l == ql && r == qr) {
			add(root, l, r, val);
			return;
		}
		pushdown(root, l, r);
		int mid = (l + r) / 2;
		if (mid >= ql) add(a[root].lc, l, mid, ql, min(mid, qr), val);
		if (mid + 1 <= qr) add(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, val);
		update(root);
	}
	void add(int l, int r, long long val) {
		add(root, 1, n, l, r, val);
	}
	info query(int root, int l, int r, int ql, int qr) {
		if (l == ql && r == qr) return a[root].now;
		pushdown(root, l, r);
		int mid = (l + r) / 2;
		if (ql > mid) return query(a[root].rc, mid + 1, r, ql, qr);
		if (mid >= qr) return query(a[root].lc, l, mid, ql, qr);
		return query(a[root].lc, l, mid, ql, mid) + query(a[root].rc, mid + 1, r, mid + 1, qr);
	}
	int query(int l, int r) {
		info tmp = query(root, 1, n, l, r);
		if (tmp.Min == 0) return tmp.Cnt;
		else return 0;
	}
	void pushdown(int root, int l, int r) {
		if (a[root].lc == 0) return;
		if (a[root].delta) {
			if (a[a[root].lc].now.Min == a[a[root].rc].now.Min) {
				a[a[root].lc].delta += a[root].delta;
				if (a[a[root].lc].tag == 1) a[a[root].lc].val += a[root].delta;
				else a[a[root].lc].now.Min += a[root].delta;
				a[a[root].rc].delta += a[root].delta;
				if (a[a[root].rc].tag == 1) a[a[root].rc].val += a[root].delta;
				else a[a[root].rc].now.Min += a[root].delta;
			} else if (a[a[root].lc].now.Min < a[a[root].rc].now.Min) {
				a[a[root].lc].delta += a[root].delta;
				if (a[a[root].lc].tag == 1) a[a[root].lc].val += a[root].delta;
				else a[a[root].lc].now.Min += a[root].delta;
			} else {
				a[a[root].rc].delta += a[root].delta;
				if (a[a[root].rc].tag == 1) a[a[root].rc].val += a[root].delta;
				else a[a[root].rc].now.Min += a[root].delta;
			}
			a[root].delta = 0;
		}
		int mid = (l + r) / 2;
		if (a[root].tag == 1) {
			cover(a[root].lc, l, mid, a[root].val);
			cover(a[root].rc, mid + 1, r, a[root].val);
			a[root].tag = 0;
			a[root].val = 0;
		} else if (a[root].tag == 2) {
			add(a[root].lc, l, mid, a[root].val);
			add(a[root].rc, mid + 1, r, a[root].val);
			a[root].tag = 0;
			a[root].val = 0;
		}
		update(root);
	}
} ST;
int main() {
	int n, m;
	read(n), read(m);
	for (int i = 1; i <= n; i++)
		read(ST.value[i]);
	ST.init(n);
	for (int i = 1; i <= m; i++) {
		int opt, l, r, val;
		read(opt), read(l), read(r);
		if (opt == 1) read(val), ST.cover(l, r, val);
		if (opt == 2) read(val), ST.add(l, r, val);
		if (opt == 3) writeln(ST.query(l, r));
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值