Description
给定序列 a=(a1,a2,⋯ ,an)a=(a_1,a_2,\cdots,a_n)a=(a1,a2,⋯,an),b=(b1,b2,⋯ ,bn)b=(b_1,b_2,\cdots,b_n)b=(b1,b2,⋯,bn) 和 c=(c1,c2,⋯ ,cn)c=(c_1,c_2,\cdots,c_n)c=(c1,c2,⋯,cn),有 mmm 个操作分七种:
- exca(l,r)\operatorname{exc_a}(l,r)exca(l,r):对每个 i∈[l,r]i\in[l,r]i∈[l,r] 执行 ai←ai+bia_i\gets a_i+b_iai←ai+bi.
- excb(l,r)\operatorname{exc_b}(l,r)excb(l,r):对每个 i∈[l,r]i\in[l,r]i∈[l,r] 执行 bi←bi+cib_i\gets b_i+c_ibi←bi+ci.
- excc(l,r)\operatorname{exc_c}(l,r)excc(l,r):对每个 i∈[l,r]i\in[l,r]i∈[l,r] 执行 ci←ci+aic_i\gets c_i+a_ici←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] 执行 ai←ai+va_i\gets a_i+vai←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] 执行 bi←bi×vb_i\gets b_i\times vbi←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] 执行 ci←vc_i\gets vci←v.
- query(l,r)\operatorname{query}(l,r)query(l,r):求 ∑i=lrai\sum\limits_{i=l}^r a_ii=l∑rai,∑i=lrbi\sum\limits_{i=l}^r b_ii=l∑rbi 和 ∑i=lrci\sum\limits_{i=l}^r c_ii=l∑rci的值,对 998244353998244353998244353 取模.
Limitations
1≤n,m≤2.5×1051\le n,m\le 2.5\times 10^51≤n,m≤2.5×105
0≤ai,bi,ci,v<9982443530\le a_i,b_i,c_i,v<9982443530≤ai,bi,ci,v<998244353
1≤l≤r≤n1\le l\le r\le n1≤l≤r≤n
5s,500MB5\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,01,1,0,00,0,1,00,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,00,1,0,00,1,1,00,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,00,1,0,00,0,1,00,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,00,1,0,00,0,1,0v,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,00,v,0,00,0,1,00,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,00,1,0,00,0,0,00,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]
由于矩阵乘法满足结合律,可以用线段树维护,维护每个节点的矩阵与标记(也是一个矩阵),修改操作直接乘上对应矩阵,查询操作求矩阵和即可.
需要注意几点:
- 要初始化为单位矩阵的地方,不要忘记初始化.
- 矩阵乘法时,不计算一直为 000 的位置.
- 如果是单位矩阵就不下传.
Code
3.85KB,27.95s,80.72MB (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;
}