P7453 & LOJ 2980 [THUSC 2017] 大魔法师 Solution

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_iaiai+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_ibibi+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_icici+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+vaiai+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 vbibi×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 vciv.
  • query⁡(l,r)\operatorname{query}(l,r)query(l,r):求 ∑i=lrai\sum\limits_{i=l}^r a_ii=lrai∑i=lrbi\sum\limits_{i=l}^r b_ii=lrbi∑i=lrci\sum\limits_{i=l}^r c_ii=lrci的值,对 998244353998244353998244353 取模.

Limitations

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值