UOJ 164【清华集训2015】V Solution

Description

给定序列 a=(a1,a2,⋯ ,an)a=(a_1,a_2,\cdots,a_n)a=(a1,a2,,an),另有序列 hhh,初始时 h=ah=ah=a.
mmm 个操作分五种:

  • add⁡(l,r,v)\operatorname{add}(l,r,v)add(l,r,v):对每个 i∈[l,r]i\in[l,r]i[l,r] 执行 ai←ai+va_i\gets a_i+vaiai+v.
  • subtract⁡(l,r,v)\operatorname{subtract}(l,r,v)subtract(l,r,v):对每个 i∈[l,r]i\in[l,r]i[l,r] 执行 ai←max⁡(0,ai−v)a_i\gets \max(0, a_i-v)aimax(0,aiv).
  • assign⁡(l,r,v)\operatorname{assign}(l,r,v)assign(l,r,v):对每个 i∈[l,r]i\in[l,r]i[l,r] 执行 ai←va_i\gets vaiv.
  • get⁡(i)\operatorname{get}(i)get(i):求 aia_iai.
  • geth⁡(i)\operatorname{geth}(i)geth(i):求 hih_ihi.

每次修改后,对每个 i∈[1,n]i\in[1,n]i[1,n] 执行 hi←max⁡(hi,ai)h_i\gets \max(h_i,a_i)himax(hi,ai).

Limitations

1≤n,m≤5×1051\le n,m\le 5\times 10^51n,m5×105
0≤ai,v≤1090\le a_i,v\le 10^90ai,v109
1≤l,r,i≤n1\le l,r,i\le n1l,r,in
2s,128MB2\text{s},128\text{MB}2s,128MB

Solution

显然使用线段树.
由于带有 chmax⁡\operatorname{chmax}chmax,每个节点维护标记 (k,b)(k,b)(k,b) 表示 xxx 变为 max⁡(x+k,b)\max(x+k,b)max(x+k,b).
那么三种修改可以如下表示:

  • add⁡\operatorname{add}add 操作:(v,−∞)(v,-\infty)(v,).
  • subtract⁡\operatorname{subtract}subtract 操作:(−v,0)(-v,0)(v,0).
  • assign⁡\operatorname{assign}assign 操作:(−∞,v)(-\infty,v)(,v).

由于需要求 hih_ihi,还需要维护历史值的标记 (hk,hb)(\textit{hk},\textit{hb})(hk,hb).
考虑合并,画出图像(我画不好不献丑了),可得:

  • (k0,b0)+(k1,b1)=(k0+k1,max⁡(b0+k1,b1))(k_0,b_0)+(k_1,b_1)=(k_0+k_1,\max(b_0+k_1,b_1))(k0,b0)+(k1,b1)=(k0+k1,max(b0+k1,b1)).
  • (hk0,hb0)+(hk1,hb1)=(max⁡(hk0,k0+hk1),max⁡(hb0,hb1,b0+hk1))(\textit{hk}_0,\textit{hb}_0)+(\textit{hk}_1,\textit{hb}_1)=(\max(\textit{hk}_0,k_0+\textit{hk}_1),\max(\textit{hb}_0,\textit{hb}_1,b_0+\textit{hk}_1))(hk0,hb0)+(hk1,hb1)=(max(hk0,k0+hk1),max(hb0,hb1,b0+hk1)).

查询时直接取出点 iii 的标记,计算即可.
注意幺元 e=(0,−∞)e=(0,-\infty)e=(0,),合并 kkk 时需要和 −∞-\inftymax⁡\maxmax,以及开 long long.

Code

3KB,2.93s,45.11MB  (in total, C++ 20)3\text{KB},2.93\text{s},45.11\text{MB}\;\texttt{(in total, C++ 20)}3KB,2.93s,45.11MB(in total, C++ 20)

#include <bits/stdc++.h>
using namespace std;

using i64 = long long;
using ui64 = unsigned long long;
using i128 = __int128;
using ui128 = unsigned __int128;
using f4 = float;
using f8 = double;
using f16 = long double;

template<class T>
bool chmax(T &a, const T &b){
	if(a < b){ a = b; return true; }
	return false;
}

template<class T>
bool chmin(T &a, const T &b){
	if(a > b){ a = b; return true; }
	return false;
}

constexpr i64 inf = 2e18;
namespace seg_tree {
	struct Tag {
		i64 hk, hb, k, b;
		inline Tag() : hk(0), hb(-inf), k(0), b(-inf) {}
		inline Tag(i64 _hk, i64 _hb, i64 _k, i64 _b) : hk(_hk), hb(_hb), k(_k), b(_b) {}
		
		inline void apply(const Tag& t) {
			hk = max(hk, k + t.hk);
			hb = max(hb, max(b + t.hk, t.hb));
			k = max(k + t.k, -inf);
			b = max(b + t.k, t.b);
		}
		
		inline i64 f(i64 x) { return max(k + x, b); }
		inline i64 g(i64 x) { return max(hk + x, hb); }
	};
	
	struct Node {
		int l, r;
		Tag tag;
	};
	
	inline int ls(int u) { return 2 * u + 1; }
	inline int rs(int u) { return 2 * u + 2; }
	
	struct SegTree {
		vector<Node> tr;
		inline SegTree() {}
		inline SegTree(int n) : tr(n << 1) { build(0, 0, n - 1); }
		
		inline void pushdown(int u, int mid) {
			tr[ls(mid)].tag.apply(tr[u].tag);
			tr[rs(mid)].tag.apply(tr[u].tag);
			tr[u].tag = Tag();
		}
		
		inline void build(int u, int l, int r) {
			tr[u].l = l, tr[u].r = r, tr[u].tag = Tag();
			if (l == r) return;
			const int mid = (l + r) >> 1;
			build(ls(mid), l, mid), build(rs(mid), mid + 1, r);
		}
		
		inline void update(int u, int l, int r, i64 k, i64 b) {
			if (l <= tr[u].l && tr[u].r <= r) return tr[u].tag.apply(Tag(k, b, k, b));
			const int mid = (tr[u].l + tr[u].r) >> 1;
			pushdown(u, mid);
			if (l <= mid) update(ls(mid), l, r, k, b);
			if (r > mid) update(rs(mid), l, r, k, b);
		}
		
		inline Tag get(int u, int p) {
			if (tr[u].l == tr[u].r) return tr[u].tag;
			const int mid = (tr[u].l + tr[u].r) >> 1;
			pushdown(u, mid);
			if (p <= mid) return get(ls(mid), p);
			else return get(rs(mid), p);
		}
		
		inline void range_add(int l, int r, i64 x) { update(0, l, r, x, -inf); }
		inline void range_sub(int l, int r, i64 x) { update(0, l, r, -x, 0); }
		inline void range_assign(int l, int r, i64 x) { update(0, l, r, -inf, x); }
		inline Tag get(int p) { return get(0, p); }
	};
}
using seg_tree::SegTree;

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	
	int n, m;
	scanf("%d %d", &n, &m);
	
	vector<i64> a(n);
	for (int i = 0; i < n; i++) scanf("%lld", &a[i]);
	
	SegTree sgt(n);
	for (int i = 0, op; i < m; i++) {
		scanf("%d", &op);
		if (op <= 3) {
			int l, r; i64 k;
			scanf("%d %d %lld", &l, &r, &k), l--, r--;
			if (op == 1) sgt.range_add(l, r, k);
			if (op == 2) sgt.range_sub(l, r, k);
			if (op == 3) sgt.range_assign(l, r, k);
		}
		else {
			int x; scanf("%d", &x), x--;
			auto tag = sgt.get(x);
			if (op == 4) printf("%lld\n", tag.f(a[x]));
			if (op == 5) printf("%lld\n", tag.g(a[x]));
		}
	}
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值