Description
给定序列 a = ( a 1 , a 2 , ⋯ , a n ) a=(a_1,a_2,\cdots,a_n) a=(a1,a2,⋯,an), b = ( b 1 , b 2 , ⋯ , b n ) b=(b_1,b_2,\cdots,b_n) b=(b1,b2,⋯,bn) 和 c = ( c 1 , c 2 , ⋯ , c n ) c=(c_1,c_2,\cdots,c_n) c=(c1,c2,⋯,cn),有 m m m 个操作分七种:
- e x c a ( l , r ) \operatorname{exc_a}(l,r) exca(l,r):对每个 i ∈ [ l , r ] i\in[l,r] i∈[l,r] 执行 a i ← a i + b i a_i\gets a_i+b_i ai←ai+bi.
- e x c b ( l , r ) \operatorname{exc_b}(l,r) excb(l,r):对每个 i ∈ [ l , r ] i\in[l,r] i∈[l,r] 执行 b i ← b i + c i b_i\gets b_i+c_i bi←bi+ci.
- e x c c ( l , r ) \operatorname{exc_c}(l,r) excc(l,r):对每个 i ∈ [ l , r ] i\in[l,r] i∈[l,r] 执行 c i ← c i + a i c_i\gets c_i+a_i ci←ci+ai.
- add ( l , r , v ) \operatorname{add}(l,r,v) add(l,r,v):对每个 i ∈ [ l , r ] i\in[l,r] i∈[l,r] 执行 a i ← a i + v a_i\gets a_i+v ai←ai+v.
- mul ( l , r , v ) \operatorname{mul}(l,r,v) mul(l,r,v):对每个 i ∈ [ l , r ] i\in[l,r] i∈[l,r] 执行 b i ← b i × v b_i\gets b_i\times v bi←bi×v.
- assign ( l , r , v ) \operatorname{assign}(l,r,v) assign(l,r,v):对每个 i ∈ [ l , r ] i\in[l,r] i∈[l,r] 执行 c i ← v c_i\gets v ci←v.
- query ( l , r ) \operatorname{query}(l,r) query(l,r):求 ∑ i = l r a i \sum\limits_{i=l}^r a_i i=l∑rai, ∑ i = l r b i \sum\limits_{i=l}^r b_i i=l∑rbi 和 ∑ i = l r c i \sum\limits_{i=l}^r c_i i=l∑rci的值,对 998244353 998244353 998244353 取模.
Limitations
1
≤
n
,
m
≤
2.5
×
1
0
5
1\le n,m\le 2.5\times 10^5
1≤n,m≤2.5×105
0
≤
a
i
,
b
i
,
c
i
,
v
<
998244353
0\le a_i,b_i,c_i,v<998244353
0≤ai,bi,ci,v<998244353
1
≤
l
≤
r
≤
n
1\le l\le r\le n
1≤l≤r≤n
5
s
,
500
MB
5\text{s},500\text{MB}
5s,500MB
Solution
显然需要矩阵,由于要加常数,每个节点维护矩阵
[
a
,
b
,
c
,
1
]
\begin{bmatrix}a,b,c,1\end{bmatrix}
[a,b,c,1].
然后考虑用矩阵表达修改,由左行右列的口诀,显然有:
- [ a , b , c , 1 ] × [ 1 , 0 , 0 , 0 1 , 1 , 0 , 0 0 , 0 , 1 , 0 0 , 0 , 0 , 1 ] = [ a + b , b , c , 1 ] \begin{bmatrix}a,b,c,1\end{bmatrix}\times \begin{bmatrix}1,0,0,0\\ 1,1,0,0\\0,0,1,0\\0,0,0,1\end{bmatrix}=\begin{bmatrix}a+b,b,c,1\end{bmatrix} [a,b,c,1]× 1,0,0,01,1,0,00,0,1,00,0,0,1 =[a+b,b,c,1]
- [ a , b , c , 1 ] × [ 1 , 0 , 0 , 0 0 , 1 , 0 , 0 0 , 1 , 1 , 0 0 , 0 , 0 , 1 ] = [ a , b + c , c , 1 ] \begin{bmatrix}a,b,c,1\end{bmatrix}\times \begin{bmatrix}1,0,0,0\\ 0,1,0,0\\0,1,1,0\\0,0,0,1\end{bmatrix}=\begin{bmatrix}a,b+c,c,1\end{bmatrix} [a,b,c,1]× 1,0,0,00,1,0,00,1,1,00,0,0,1 =[a,b+c,c,1]
- [ a , b , c , 1 ] × [ 1 , 0 , 1 , 0 0 , 1 , 0 , 0 0 , 0 , 1 , 0 0 , 0 , 0 , 1 ] = [ a , b , c + a , 1 ] \begin{bmatrix}a,b,c,1\end{bmatrix}\times \begin{bmatrix}1,0,1,0\\ 0,1,0,0\\0,0,1,0\\0,0,0,1\end{bmatrix}=\begin{bmatrix}a,b,c+a,1\end{bmatrix} [a,b,c,1]× 1,0,1,00,1,0,00,0,1,00,0,0,1 =[a,b,c+a,1]
- [ a , b , c , 1 ] × [ 1 , 0 , 0 , 0 0 , 1 , 0 , 0 0 , 0 , 1 , 0 v , 0 , 0 , 1 ] = [ a + v , b , c , 1 ] \begin{bmatrix}a,b,c,1\end{bmatrix}\times \begin{bmatrix}1,0,0,0\\ 0,1,0,0\\0,0,1,0\\v,0,0,1\end{bmatrix}=\begin{bmatrix}a+v,b,c,1\end{bmatrix} [a,b,c,1]× 1,0,0,00,1,0,00,0,1,0v,0,0,1 =[a+v,b,c,1]
- [ a , b , c , 1 ] × [ 1 , 0 , 0 , 0 0 , v , 0 , 0 0 , 0 , 1 , 0 0 , 0 , 0 , 1 ] = [ a , b × v , c , 1 ] \begin{bmatrix}a,b,c,1\end{bmatrix}\times \begin{bmatrix}1,0,0,0\\ 0,v,0,0\\0,0,1,0\\0,0,0,1\end{bmatrix}=\begin{bmatrix}a,b\times v,c,1\end{bmatrix} [a,b,c,1]× 1,0,0,00,v,0,00,0,1,00,0,0,1 =[a,b×v,c,1]
- [ a , b , c , 1 ] × [ 1 , 0 , 0 , 0 0 , 1 , 0 , 0 0 , 0 , 0 , 0 0 , 0 , v , 1 ] = [ a , b , v , 1 ] \begin{bmatrix}a,b,c,1\end{bmatrix}\times \begin{bmatrix}1,0,0,0\\ 0,1,0,0\\0,0,0,0\\0,0,v,1\end{bmatrix}=\begin{bmatrix}a,b,v,1\end{bmatrix} [a,b,c,1]× 1,0,0,00,1,0,00,0,0,00,0,v,1 =[a,b,v,1]
由于矩阵乘法满足结合律,可以用线段树维护,维护每个节点的矩阵与标记(也是一个矩阵),修改操作直接乘上对应矩阵,查询操作求矩阵和即可.
需要注意几点:
- 要初始化为单位矩阵的地方,不要忘记初始化.
- 矩阵乘法时,不计算一直为 0 0 0 的位置.
- 如果是单位矩阵就不下传.
Code
3.85 KB , 27.95 s , 80.72 MB (in total, C++20 with O2) 3.85\text{KB},27.95\text{s},80.72\text{MB}\;\texttt{(in total, C++20 with O2)} 3.85KB,27.95s,80.72MB(in total, C++20 with O2)
#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 = 998244353;
inline int add(int x, int y) {return x + y >= mod ? x + y - mod : x + y; }
namespace matrix {
struct Mat {
int mat[4][4];
inline Mat(int _e = 1) {
memset(mat, 0, sizeof mat);
mat[0][0] = mat[1][1] = mat[2][2] = mat[3][3] = _e;
}
inline int* operator[](int i) { return mat[i]; }
inline const int* operator[](int i) const { return mat[i]; }
};
inline Mat operator+(const Mat& x, const Mat& y) {
Mat z(0);
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++) z[i][j] = add(x[i][j], y[i][j]);
return z;
}
inline Mat operator*(const Mat& x, const Mat& y) {
Mat z(0);
for (int i = 0; i < 4; i++)
for (int k = 0; k < 4; k++) {
if (!x[i][k]) continue;
for (int j = 0; j < 4; j++) {
if (!y[k][j]) continue;
z[i][j] = (z[i][j] + 1LL * x[i][k] * y[k][j]) % mod;
}
}
return z;
}
inline bool operator==(const Mat& x, const Mat& y) {
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
if (x[i][j] != y[i][j]) return false;
return true;
}
}
using matrix::Mat;
namespace seg_tree {
struct Node {
int l, r;
Mat val, 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(const vector<Mat>& a) {
const int n = a.size();
tr.resize(n << 1);
build(0, 0, n - 1, a);
}
inline void pushup(int u, int mid) {
tr[u].val = tr[ls(mid)].val + tr[rs(mid)].val;
}
inline void apply(int u, const Mat& mat) {
tr[u].val = tr[u].val * mat;
tr[u].tag = tr[u].tag * mat;
}
inline void pushdown(int u, int mid) {
if (tr[u].tag == Mat()) return;
apply(ls(mid), tr[u].tag);
apply(rs(mid), tr[u].tag);
tr[u].tag = Mat();
}
inline void build(int u, int l, int r, const vector<Mat>& a) {
tr[u].l = l, tr[u].r = r;
if (l == r) {
tr[u].val = a[l];
return;
}
const int mid = (l + r) >> 1;
build(ls(mid), l, mid, a);
build(rs(mid), mid + 1, r, a);
pushup(u, mid);
}
inline void modify(int u, int l, int r, const Mat& mat) {
if (l <= tr[u].l && tr[u].r <= r) return apply(u, mat);
const int mid = (tr[u].l + tr[u].r) >> 1;
pushdown(u, mid);
if (l <= mid) modify(ls(mid), l, r, mat);
if (r > mid) modify(rs(mid), l, r, mat);
pushup(u, mid);
}
inline Mat query(int u, int l, int r) {
if (l <= tr[u].l && tr[u].r <= r) return tr[u].val;
const int mid = (tr[u].l + tr[u].r) >> 1;
Mat res = Mat(0);
pushdown(u, mid);
if (l <= mid) res = res + query(ls(mid), l, r);
if (r > mid) res = res + query(rs(mid), l, r);
return res;
}
};
}
using seg_tree::SegTree;
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n; scanf("%d", &n);
vector<Mat> a(n);
for (int i = 0; i < n; i++) {
scanf("%d %d %d", &a[i][0][0], &a[i][0][1], &a[i][0][2]);
a[i][0][3] = 1;
}
SegTree sgt(a);
int m; scanf("%d", &m);
for (int i = 0, op, l, r; i < m; i++) {
scanf("%d %d %d", &op, &l, &r), l--, r--;
if (op == 7) {
auto mat = sgt.query(0, l, r);
printf("%d %d %d\n", mat[0][0], mat[0][1], mat[0][2]);
}
else {
auto mat = Mat();
if (op == 1) mat[1][0] = 1;
if (op == 2) mat[2][1] = 1;
if (op == 3) mat[0][2] = 1;
if (op == 4) scanf("%d", &mat[3][0]);
if (op == 5) scanf("%d", &mat[1][1]);
if (op == 6) scanf("%d", &mat[3][2]), mat[2][2] = 0;
sgt.modify(0, l, r, mat);
}
}
return 0;
}