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 写到了一起.
#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;
}