P8310 〈 TREEのOI 2022 Spring 〉Essential Operations Solution

Description

给定序列 a = ( a 1 , a 2 , ⋯   , a n ) a=(a_1,a_2,\cdots,a_n) a=(a1,a2,⋯,an),有 m m m 个操作分四种:

  • add ⁡ ( l , r , k ) \operatorname{add}(l,r,k) add(l,r,k):对每个 i ∈ [ l , r ] i\in[l,r] i∈[l,r] 执行 a i ← a i + k a_i\gets a_i+k ai←ai+k.
  • mul ⁡ ( l , r , k ) \operatorname{mul}(l,r,k) mul(l,r,k):对每个 i ∈ [ l , r ] i\in[l,r] i∈[l,r] 执行 a i ← a i × k a_i\gets a_i\times k ai←ai×k.
  • query ⁡ ( l , r ) \operatorname{query}(l,r) query(l,r):求 ( ∑ i = l r a i )   m o d   19260817 (\sum_{i=l}^r a_i) \bmod 19260817 (∑i=lrai)mod19260817.
  • roll ⁡ ( ) \operatorname{roll}() roll():将序列回溯到上次 roll ⁡ \operatorname{roll} roll 操作 (若没有则为初始状态),并倒序执行 这两次操作间的所有修改操作.

Limitations

1 ≤ n ≤ 5 × 1 0 5 1\le n\le 5\times 10^5 1≤n≤5×105
1 ≤ m ≤ 1 0 5 1\le m\le 10^5 1≤m≤105
1 ≤ a i , x ≤ 1 0 3 1\le a_i,x \le 10^3 1≤ai,x≤103
0.5 s , 45 MB 0.5\text{s},\textcolor{red}{45\text{MB}} 0.5s,45MB

Solution

没有 roll ⁡ \operatorname{roll} roll 操作大家都会做吧.

就算需要回溯,也可用主席树完成,但是刁钻的空间限制让其不能胜任.

但可以发现,我们只需要回溯到上次 roll ⁡ \operatorname{roll} roll 操作前,因此我们只需维护上次 roll ⁡ \operatorname{roll} roll 操作前的数组.

但只开一棵线段树无法快速回溯,考虑再开一棵.

前 3 3 3 个操作依旧用第一棵树完成, roll ⁡ \operatorname{roll} roll 时将两棵树交换 ,并倒序执行修改 .

容易发现,此时的另一棵树恰好是 roll ⁡ \operatorname{roll} roll 前的状态.

由于每个操作最多被执行两次,时间复杂度仍为 O ( m log ⁡ n ) O(m\log n) O(mlogn).

需要注意几点:

  • 线段树 Node 上不要存 l , r l,r l,r,建议使用两倍空间写法.
  • 不能开 long long,乘法时强转即可.
  • 倒序时只用执行修改.
  • 两棵树都要 build.

Code

3.44 KB , 3.47 s , 26.83 MB    (in total, C++20 with O2) 3.44\text{KB},3.47\text{s},26.83\text{MB}\;\texttt{(in total, C++20 with O2)} 3.44KB,3.47s,26.83MB(in total, C++20 with O2)

由于偷懒,在线段树中我把 add ⁡ \operatorname{add} add 和 mul ⁡ \operatorname{mul} mul 写到了一起.

cpp 复制代码
#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 int mod = 19260817;
inline int add(int x, int y) { return (x + y >= mod) ? (x + y - mod) : (x + y); }
inline int mul(int x, int y) { return 1LL * x * y % mod; }
inline int ls(int u) { return 2 * u + 1; }
inline int rs(int u) { return 2 * u + 2; }

struct SegTree {
	struct Node {
		int sum, mul, add;
	};
	vector<Node> tr;
	inline SegTree() {}
	inline SegTree(const vector<int>& a) {
		const int n = a.size();
		tr.resize(n << 1);      // 2x memory
		build(0, 0, n - 1, a);
	}
	
	inline void pushup(int u, int mid) {
		tr[u].sum = add(tr[ls(mid)].sum, tr[rs(mid)].sum);
	}
	
	inline void apply(int u, int tag_mul, int tag_add, int len) {
		if (tag_mul != 1) {
			tr[u].sum = mul(tr[u].sum, tag_mul);
			tr[u].mul = mul(tr[u].mul, tag_mul);
			tr[u].add = mul(tr[u].add, tag_mul);
		}
		if (tag_add != 0) {
			tr[u].sum = add(tr[u].sum, mul(tag_add, len));
			tr[u].add = add(tr[u].add, tag_add);
		}
	}
	
	inline void pushdown(int u, int l, int mid, int r) {
		apply(ls(mid), tr[u].mul, tr[u].add, mid - l + 1);
		apply(rs(mid), tr[u].mul, tr[u].add, r - mid);
		tr[u].mul = 1, tr[u].add = 0;
	}
	
	inline void build(int u, int l, int r, const vector<int>& a) {
		tr[u].mul = 1, tr[u].add = 0;
		if (l == r) return (void)(tr[u].sum = a[l]);
		const int mid = (l + r) >> 1;
		build(ls(mid), l, mid, a), build(rs(mid), mid + 1, r, a);
		pushup(u, mid);
	}
	
	// Preform `a_i = a_i * mul + add` for each i in range [l, r].
	inline void update(int u, int l, int r, int L, int R, int mul, int add) {
		if (L <= l && r <= R) return apply(u, mul, add, r - l + 1);
		if (l > R || r < L) return;
		const int mid = (l + r) >> 1;
		pushdown(u, l, mid, r);
		update(ls(mid), l, mid, L, R, mul, add);
		update(rs(mid), mid + 1, r, L, R, mul, add);
		pushup(u, mid);
	}
	
	inline int query(int u, int l, int r, int L, int R) {
		if (L <= l && r <= R) return tr[u].sum;
		if (l > R || r < L) return 0;
		const int mid = (l + r) >> 1;
		pushdown(u, l, mid, r);
		return add(query(ls(mid), l, mid, L, R), query(rs(mid), mid + 1, r, L, R));
	}
};

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	
	int n, m; scanf("%d %d", &n, &m);
	vector<int> a(n);
	for (int i = 0; i < n; i++) scanf("%d", &a[i]);
	array<SegTree, 2> sgt{SegTree(a), SegTree(a)};
	
	auto range_add = [&](int now, int l, int r, int x) {
		sgt[now].update(0, 0, n - 1, l, r, 1, x);
	};
	auto range_mul = [&](int now, int l, int r, int x) {
		sgt[now].update(0, 0, n - 1, l, r, x, 0);
	};
	auto range_sum = [&](int now, int l, int r) {
		return sgt[now].query(0, 0, n - 1, l, r);
	};
	
	vector<int> op(m), L(m), R(m), X(m);
	for (int i = 0, now = 0; i < m; i++) {
		scanf("%d", &op[i]);
		if (op[i] == 4) {
			now ^= 1;
			for (int j = i - 1; j >= 0; j--) {
				if (op[j] == 4) break;
				else if (op[j] == 3) continue;
				else if (op[j] == 1) range_add(now, L[j], R[j], X[j]);
				else range_mul(now, L[j], R[j], X[j]);
			}
		}
		else {
			scanf("%d %d", &L[i], &R[i]), L[i]--, R[i]--;
			if (op[i] == 3) printf("%d\n", range_sum(now, L[i], R[i]));
			else {
				scanf("%d", &X[i]);
				if (op[i] == 1) range_add(now, L[i], R[i], X[i]);
				else range_mul(now, L[i], R[i], X[i]);
			}
		}
	}
	
	return 0;
}
相关推荐
小竹子1411 分钟前
L1-1 天梯赛座位分配
数据结构·c++·算法
董董灿是个攻城狮20 分钟前
Transformer 通关秘籍8:词向量如何表示近义词?
算法
独好紫罗兰42 分钟前
洛谷题单2-P5712 【深基3.例4】Apples-python-流程图重构
开发语言·python·算法
uhakadotcom1 小时前
NVIDIA Resiliency Extension(NVRx)简介:提高PyTorch训练的容错性
算法·面试·github
梭七y1 小时前
【力扣hot100题】(020)搜索二维矩阵Ⅱ
算法·leetcode·职场和发展
v维焓1 小时前
C++(思维导图更新)
开发语言·c++·算法
ylfhpy2 小时前
Java面试黄金宝典22
java·开发语言·算法·面试·职场和发展
Phoebe鑫2 小时前
数据结构每日一题day9(顺序表)★★★★★
数据结构·算法
CYRUS_STUDIO2 小时前
Frida Hook Native:jobjectArray 参数解析
android·c++·逆向
榆榆欸2 小时前
4.Socket类、InetAddr类、Epoll类实现模块化
linux·c++·tcp/ip