【BZOJ5117】【UOJ164】【清华集训2015】V

【题目链接】

【思路要点】

  • 首先定义一种标记\((a,b)\)表示先给这个区间的数加上\(a\)再和\(b\)取最大值。
  • 不难发现题目中的操作均可以表示成\((a,b)\)的形式,分别是\((x,0),(-x,0),(-INF,x)\)。
  • 考虑合并标记\((a,b),(c,d)\),那么得到的是\((max(a+c,-INF),max(b+c,d))\),对于4号询问,我们只需要将标记依次下传至叶子结点即可回答询问。
  • 现在考虑历史最大值标记,我们可以把标记看成一个函数,每一个数字对应的函数值表示该数字作用该标记后的答案。不难发现该函数是一个分段函数,由两段斜率分别为0和1的直线组成。“历史最大值”标记定义为“从上次下传该点标记至今,标记达到的最大情况”,其中“最大情况”对于每一个数字是独立的。
  • 在更新历史最大值标记时,我们相当于将两个函数画出来,取靠上方的一段。下图展示的是一种可能的情况,其中蓝色部分代表合并的函数,红色部分代表合并的结果。
  • 因此,该标记同样可以合并,在询问5时只需将标记下传至叶子结点即可回答询问。
  • 时间复杂度\(O(MLogN)\)。
  • 题解原文见国家集训队2016论文集之《区间最值操作与历史最值问题——杭州学军中学 吉如一》。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 500005;
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 Add, Max; };
info operator * (info a, info b) {
	info ans;
	ans.Add = max(a.Add + b.Add, -INF);
	ans.Max = max(a.Max + b.Add, b.Max);
	return ans;
}
info operator + (info a, info b) {
	info ans;
	ans.Add = max(a.Add, b.Add);
	ans.Max = max(a.Max, b.Max);
	return ans;
}
long long operator * (int a, info b) {
	return max(a + b.Add, b.Max);
}
struct Segment_Tree {
	struct Node {
		int lc, rc;
		info now, his;
	} a[MAXN * 2];
	int n, root, size, val[MAXN];
	void build(int &root, int l, int r) {
		root = ++size;
		if (l == r) return;
		int mid = (l + r) / 2;
		build(a[root].lc, l, mid);
		build(a[root].rc, mid + 1, r);
	}
	void init(int x) {
		n = x;
		root = size = 0;
		build(root, 1, n);
	}
	void pushdown(int root) {
		a[a[root].lc].his = a[a[root].lc].his + a[a[root].lc].now * a[root].his;
		a[a[root].lc].now = a[a[root].lc].now * a[root].now;
		a[a[root].rc].his = a[a[root].rc].his + a[a[root].rc].now * a[root].his;
		a[a[root].rc].now = a[a[root].rc].now * a[root].now;
		a[root].now = (info) {0, 0};
		a[root].his = (info) {0, 0};
	}
	void add(int root, int l, int r, int ql, int qr, info x) {
		if (l == ql && r == qr) {
			a[root].his = a[root].his + a[root].now * x;
			a[root].now = a[root].now * x;
			return;
		}
		pushdown(root);
		int mid = (l + r) / 2;
		if (mid >= ql) add(a[root].lc, l, mid, ql, min(mid, qr), x);
		if (mid + 1 <= qr) add(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, x);
	}
	void add(int l, int r, long long x, long long y) {
		add(root, 1, n, l, r, (info) {x, y});
	}
	long long query(int root, int l, int r, int x) {
		if (l == r) return val[x] * a[root].now;
		pushdown(root);
		int mid = (l + r) / 2;
		if (mid >= x) return query(a[root].lc, l, mid, x);
		else return query(a[root].rc, mid + 1, r, x);
	}
	long long query(int x) {
		return query(root, 1, n, x);
	}
	long long history(int root, int l, int r, int x) {
		if (l == r) return val[x] * a[root].his;
		pushdown(root);
		int mid = (l + r) / 2;
		if (mid >= x) return history(a[root].lc, l, mid, x);
		else return history(a[root].rc, mid + 1, r, x);
	}
	long long history(int x) {
		return history(root, 1, n, x);
	}
} ST;
int main() {
	int n, m;
	read(n), read(m);
	for (int i = 1; i <= n; i++)
		read(ST.val[i]);
	ST.init(n);
	for (int i = 1; i <= m; i++) {
		int opt; read(opt);
		if (opt <= 3) {
			int l, r, x;
			read(l), read(r), read(x);
			if (opt == 1) ST.add(l, r, x, 0);
			if (opt == 2) ST.add(l, r, -x, 0);
			if (opt == 3) ST.add(l, r, -INF, x);
		} else {
			int x; read(x);
			if (opt == 4) writeln(ST.query(x));
			if (opt == 5) writeln(ST.history(x));;
		}
	}
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值