[THUSCH2017] 大魔法师 TJ
[THUSCH2017] 大魔法师 TJ
tad 压榨 OIer!
我们来看到这道鬼畜大师,它中二了一大堆,意思就这样:
给定 n 个元素,每个元素包含三个值 \(A,B,C\)。
进行 \(m\) 次区间操作,每次可以是以下七种之一:
- 对于区间内每个元素,令 \(A=A+B\)。
- 对于区间内每个元素,令 \(B=B+C\)。
- 对于区间内每个元素,令 \(C=C+A\)。
- 对于区间内每个元素,令 \(A=A+v\),其中 \(v\) 为一给定值,每次不同。
- 对于区间内每个元素,令 \(B=B\times v\),其中 \(v\) 为一给定值,每次不同。
- 对于区间内每个元素,令 \(C=v\),其中 \(v\) 为一给定值,每次不同。
- 查询区间内每个元素 \(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;
}