[THUSCH2017] 大魔法师 TJ

[THUSCH2017] 大魔法师 TJ

tad 压榨 OIer!

我们来看到这道鬼畜大师,它中二了一大堆,意思就这样:

给定 n 个元素,每个元素包含三个值 \(A,B,C\)

进行 \(m\) 次区间操作,每次可以是以下七种之一:

  1. 对于区间内每个元素,令 \(A=A+B\)
  2. 对于区间内每个元素,令 \(B=B+C\)
  3. 对于区间内每个元素,令 \(C=C+A\)
  4. 对于区间内每个元素,令 \(A=A+v\),其中 \(v\) 为一给定值,每次不同。
  5. 对于区间内每个元素,令 \(B=B\times v\),其中 \(v\) 为一给定值,每次不同。
  6. 对于区间内每个元素,令 \(C=v\),其中 \(v\) 为一给定值,每次不同。
  7. 查询区间内每个元素 \(A,B,C\) 累加得到的和。

其中 \(1\le n\le 2.5\times10^5\)\(1\le m\le 2.5\times 10^5\),元素中每个值对 \(998244353\) 取模。

看到这么个区间操作的问题,我们自然会想到线段树。

然而由于它形式多样,复杂多变,我们需要维护大量信息,而且出现了不同优先级的运算符号(\(+,\times,=\)),极易在计算顺序上出错。

所以我们要将他转化为一个较为统一的格式,结合题目所在位置和 luogufs 分析,那么自然采用矩阵乘法。

警告,本部分将会有一段无意义的推导,尽管它是正解的一部分,单纯看公式的自行下滑

分析一

我们将每个元素直接作为要维护的矩阵,则得到一个矩阵:\(\begin{bmatrix}A & B & C\end{bmatrix}\)

然后我们就需要为每个操作设计转移矩阵。

操作一:对于区间内每个元素,令 \(A=A+B\)

首先介绍一下单位矩阵,它从左上到右下的对角线上的值均为 \(1\),这样可以做到和原矩阵相乘不产生影响的情况。

在这个题中,我们的单位矩阵长这样:

\[\begin{bmatrix} 1 & 0 & 0\\\ 0 & 1 & 0\\\ 0 & 0 & 1 \end{bmatrix} \]

考虑操作一带来的影响,让计算 \(A\) 时累加 \(B\) 的值,则只需将矩阵修改为:

\[\begin{bmatrix} 1 & 0 & 0\\\ 1 & 1 & 0\\\ 0 & 0 & 1 \end{bmatrix} \]

操作二:对于区间内每个元素,令 \(B=B+C\)

同理,修改为:

\[\begin{bmatrix} 1 & 0 & 0\\\ 0 & 1 & 0\\\ 0 & 1 & 1 \end{bmatrix} \]

操作三:对于区间内每个元素,令 \(C=C+A\)

同理,修改为:

\[\begin{bmatrix} 1 & 0 & 1\\\ 0 & 1 & 0\\\ 0 & 0 & 1 \end{bmatrix} \]

操作四:对于区间内每个元素,令 \(A=A+v\),其中 \(v\) 为一给定值,每次不同。

此时你会发现我们进行不下去了,这个 \(v\) 无法累加到答案中。

那么我们现在面临一个常数项的问题,对于这种问题,通常我们会采取增加维度的方法。

分析二

恭喜直接下来的你被骗了,请翻回去看操作一二三的矩阵并自己写出

现在我们为矩阵增加维度,它多包含了一个常数 \(1\)\(\begin{bmatrix}A & B & C & 1\end{bmatrix}\)

操作一:对于区间内每个元素,令 \(A=A+B\)

略。

操作二:对于区间内每个元素,令 \(B=B+C\)

略。

操作三:对于区间内每个元素,令 \(C=C+A\)

略。

操作四:对于区间内每个元素,令 \(A=A+v\),其中 \(v\) 为一给定值,每次不同。

我们同理,用单位矩阵保证其他元素值不改变,现在对于某一元素的操作,只需修改它所在列。

\[\begin{bmatrix} 1 & 0 & 0 & 0\\\ 0 & 1 & 0 & 0\\\ 0 & 0 & 1 & 0\\\ 0 & 0 & 0 & 1 \end{bmatrix} \]

那么我们我们就不能简单的将 \(1\) 累加了,而是让 \(1\)\(1\times v\) 的形式累加入 \(A\)。现在有矩阵:

\[\begin{bmatrix} 1 & 0 & 0 & 0\\\ 0 & 1 & 0 & 0\\\ 0 & 0 & 1 & 0\\\ v & 0 & 0 & 1 \end{bmatrix} \]

操作五:对于区间内每个元素,令 \(B=B\times v\),其中 \(v\) 为一给定值,每次不同。

这个并不需要常数项的累加,我们依然套用单位矩阵即可。

\[\begin{bmatrix} 1 & 0 & 0 & 0\\\ 0 & v & 0 & 0\\\ 0 & 0 & 1 & 0\\\ 0 & 0 & 0 & 1 \end{bmatrix} \]

操作六:对于区间内每个元素,令 \(C=v\),其中 \(v\) 为一给定值,每次不同。

自己尝试仿照操作五推导。

Tips:舍弃一些值

操作七:查询区间内每个元素 \(A,B,C\) 累加得到的和。

这个好办啊,我们只需要对查询过程中所有的矩阵进行矩阵加法,从加起来后得到的矩阵提出 \(A,B,C\) 各自的和加起来即可。

Code:

顺口说一句,不要嫌弃我码风!

不要嫌弃我码风!

不要嫌弃我码风!

不要嫌弃我码风!

重要的事情说三遍(逃

我自己看了一下,我的码风其实也还好。

泛型、封装之类的做得也到位。

变量的作用域尽量有约束,变量名和函数名尽量也写得简洁易懂。

甚至卡常的代码为了规范,故意牺牲效率。

我也知道你们在吐槽我什么,不就是那堆语法嘛……

你学 OI 一年了,封装泛化命名空间都不会写,不该反省反省吗?

像那个玩 AI 的某某某,连个阶乘函数都一惊一乍

这些都是很基础的东西啊,bdfs 一下不行吗……

(逃

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD=998244353;
template<typename T=int> inline T read();
template<typename T> inline void read(T& x);
template<typename... Args> inline void read(Args& ...args);
template<int n,int m,typename Type> class Matrix{
private:
	Type v[n][m];
public:
	Matrix(){memset(v,0,sizeof v);}
	Matrix(const Type val[n][m]){memcpy(v,val,sizeof v);}
	Matrix(Type val){for(int i=0;i<n;i++) for(int j=0;j<m;j++) v[i][j]=val;}
	Type* operator[](int x){return v[x];}
	void init(){for(int i=0;i<min(n,m);i++) v[i][i]=1;}
	void init(Type val){for(int i=0;i<min(n,m);i++) v[i][i]=val;}
	Matrix<n,m,Type> operator+(Matrix it);
	void put(){
		for(int i=0;i<n;i++){
			for(int j=0;j<m;j++) printf("%d ",v[i][j]);
			puts("");
		}
	}
};
template<int n,int m,int q,typename Type> inline Matrix<n,q,Type> operator*(Matrix<n,m,Type> x,Matrix<m,q,Type> y);
template<int n,int m,typename Type> inline Matrix<m,n,Type> T(Matrix<n,m,Type> it);
template<int n,typename Type,typename Pow> inline Matrix<n,n,Type> operator^(Matrix<n,n,Type> A,Pow x);
int n,m;
Matrix<4,4,ll> A,B,C,D,E,F;
inline void init();
class SegmentTree{
private:
	struct node{
		Matrix<1,4,ll> arr;
		Matrix<4,4,ll> mul;
	};
	node tr[1000005];
	void push_up(const int rt){tr[rt].arr=tr[rt<<1].arr+tr[rt<<1|1].arr;}
	void push_down(const int rt){
		tr[rt<<1].arr=tr[rt<<1].arr*tr[rt].mul;
		tr[rt<<1|1].arr=tr[rt<<1|1].arr*tr[rt].mul;
		tr[rt<<1].mul=tr[rt<<1].mul*tr[rt].mul;
		tr[rt<<1|1].mul=tr[rt<<1|1].mul*tr[rt].mul;
		tr[rt].mul=Matrix<4,4,ll>();
		tr[rt].mul.init();
	}
	void build(const int rt,const int l,const int r){
		tr[rt].mul=Matrix<4,4,ll>(),tr[rt].mul.init();
		if(l==r){
			read(tr[rt].arr[0][0],tr[rt].arr[0][1],tr[rt].arr[0][2]),tr[rt].arr[0][3]=1;
			return;
		}
		const int mid=(l+r)>>1;
		build(rt<<1,l,mid);
		build(rt<<1|1,mid+1,r);
		push_up(rt);
	}
	void update(const int rt,const int l,const int r,const int L,const int R,Matrix<4,4,ll> V){
		if(L<=l&&r<=R){
			tr[rt].mul=tr[rt].mul*V;
			tr[rt].arr=tr[rt].arr*V;
			return;
		}
		push_down(rt);
		const int mid=(l+r)>>1;
		if(L<=mid) update(rt<<1,l,mid,L,R,V);
		if(R>mid) update(rt<<1|1,mid+1,r,L,R,V);
		push_up(rt);
	}
	Matrix<1,4,ll> query(const int rt,const int l,const int r,const int L,const int R){
		if(L<=l&&r<=R) return tr[rt].arr;
		push_down(rt);
		const int mid=(l+r)>>1;
		Matrix<1,4,ll> ret;
		if(L<=mid) ret=ret+query(rt<<1,l,mid,L,R);
		if(R>mid) ret=ret+query(rt<<1|1,mid+1,r,L,R);
		return ret;
	}
public:
	SegmentTree(){}
	void build(){build(1,1,n);}
	void update(const int L,const int R,Matrix<4,4,ll> V){update(1,1,n,L,R,V);}
	Matrix<1,4,ll> query(const int L,const int R){return query(1,1,n,L,R);}
};
SegmentTree Tree;
signed main(){
	init();
	read(n);
	Tree.build();
	read(m);
	for(int opt,l,r,v;m--;){
		read(opt,l,r);
		switch(opt){
			case 1:
				Tree.update(l,r,A);
				break;
			case 2:
				Tree.update(l,r,B);
				break;
			case 3:
				Tree.update(l,r,C);
				break;
			case 4:
				read(v);
				D[3][0]=v;
				Tree.update(l,r,D);
				break;
			case 5:
				read(v);
				E[1][1]=v;
				Tree.update(l,r,E);
				break;
			case 6:
				read(v);
				F[3][2]=v;
				Tree.update(l,r,F);
				break;
			case 7:
				Matrix<1,4,ll> ans=Tree.query(l,r);
				printf("%lld %lld %lld\n",ans[0][0],ans[0][1],ans[0][2]);
				break;
		}
	}
	return 0;
}

inline void init(){
	const ll 
	_A[4][4]={{1,0,0,0},{1,1,0,0},{0,0,1,0},{0,0,0,1}},
	_B[4][4]={{1,0,0,0},{0,1,0,0},{0,1,1,0},{0,0,0,1}},
	_C[4][4]={{1,0,1,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}},
	_D[4][4]={{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}},
	_E[4][4]={{1,0,0,0},{0,0,0,0},{0,0,1,0},{0,0,0,1}},
	_F[4][4]={{1,0,0,0},{0,1,0,0},{0,0,0,0},{0,0,0,1}};
	A=Matrix<4,4,ll>(_A),B=Matrix<4,4,ll>(_B),
	C=Matrix<4,4,ll>(_C),D=Matrix<4,4,ll>(_D),
	E=Matrix<4,4,ll>(_E),F=Matrix<4,4,ll>(_F);
}

template<typename T=int> inline T read(){
	T x=0;bool flag=0;char ch=getchar();
	for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') flag=1;
	if(flag) for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)-(ch&15);
	else for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch&15);
	return x;
}
template<typename T> inline void read(T& x){
	x=0;bool flag=0;char ch=getchar();
	for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') flag=1;
	if(flag) for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)-(ch&15);
	else for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch&15);
}
template<typename... Args> inline void read(Args& ...args){int arg[]{(read(args),0)...};}
template<int n,int m,typename Type> inline Matrix<n,m,Type> Matrix<n,m,Type>::operator+(Matrix it){
	Matrix<n,m,Type> ret;
	for(int i=0;i<n;i++) for(int j=0;j<m;j++) ret[i][j]=(v[i][j]+it[i][j])%MOD;
	return ret;
}
template<int n,int m,int q,typename Type> inline Matrix<n,q,Type> operator*(Matrix<n,m,Type> x,Matrix<m,q,Type> y){
	Matrix<n,q,Type> ret;
	for(int i=0;i<n;i++) for(int j=0;j<q;j++) for(int k=0;k<m;k++) ret[i][j]=(ret[i][j]+x[i][k]*y[k][j])%MOD;
	return ret;
}
template<int n,int m,typename Type> inline Matrix<m,n,Type> T(Matrix<n,m,Type> it){
	Matrix<m,n,Type> ret;
	for(int i=0;i<m;i++) for(int j=0;j<=n;j++) ret[i][j]=it[j][i];
	return ret;
}
template<int n,typename Type,typename Pow> inline Matrix<n,n,Type> operator^(Matrix<n,n,Type> A,Pow x){
	Matrix<n,n,Type> ret;
	ret.init();
	for(;x;x>>=1,A=A*A) if(x&1) ret=x*A;
	return ret;
}
posted @ 2022-12-12 20:38  LQ636721  阅读(43)  评论(0编辑  收藏  举报