P7453 [THUSC 2017] 大魔法师 Solution

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 aiai+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 bibi+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 cici+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 aiai+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 bibi×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 civ.
  • query ⁡ ( l , r ) \operatorname{query}(l,r) query(l,r):求 ∑ i = l r a i \sum\limits_{i=l}^r a_i i=lrai ∑ i = l r b i \sum\limits_{i=l}^r b_i i=lrbi ∑ i = l r c i \sum\limits_{i=l}^r c_i i=lrci的值,对 998244353 998244353 998244353 取模.

Limitations

1 ≤ n , m ≤ 2.5 × 1 0 5 1\le n,m\le 2.5\times 10^5 1n,m2.5×105
0 ≤ a i , b i , c i , v < 998244353 0\le a_i,b_i,c_i,v<998244353 0ai,bi,ci,v<998244353
1 ≤ l ≤ r ≤ n 1\le l\le r\le n 1lrn
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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值