- 有(n)个水晶球,第(i)个水晶球有三个属性(A_i,B_i,C_i)。
- (q)次区间操作,分为七种:将(A_i)加上(B_i);将(B_i)加上(C_i);将(C_i)加上(A_i);将(A_i)加上(v);将(B_i)乘上(v);将(C_i)修改为(v);求三种属性各自的和。
- (n,qle2.5 imes10^5)
线段树+矩阵乘法
一个挺有意思的技巧。
考虑我们初始在每个点维护一个矩阵:
[egin{bmatrix}
a_i&0&0&0\
0&b_i&0&0\
0&0&c_i&0\
0&0&0&1
end{bmatrix}
]
然后发现这些操作都是矩乘基本操作,只要给线段树上一段区间同时乘上一个矩阵即可。
下面分别给出每种操作对应的矩阵:
[egin{bmatrix}
1&0&0&0\
1&1&0&0\
0&0&1&0\
0&0&0&1
end{bmatrix}
egin{bmatrix}
1&0&0&0\
0&1&0&0\
0&1&1&0\
0&0&0&1
end{bmatrix}
egin{bmatrix}
1&0&1&0\
0&1&0&0\
0&0&1&0\
0&0&0&1
end{bmatrix}\
egin{bmatrix}
1&0&0&0\
0&1&0&0\
0&0&1&0\
v&0&0&1
end{bmatrix}
egin{bmatrix}
1&0&0&0\
0&v&0&0\
0&0&1&0\
0&0&0&1
end{bmatrix}
egin{bmatrix}
1&0&0&0\
0&1&0&0\
0&0&0&0\
0&0&v&1
end{bmatrix}
]
询问就是在线段树上区间求和,然后输出每列数的和即可。
代码:(O(64nlogn))
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 250000
#define X 998244353
using namespace std;
int n,a[N+5],b[N+5],c[N+5];
namespace FastIO
{
#define FS 100000
#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
Tp I void write(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);}
Tp I void writeln(Con Ty& x,Con Ty& y,Con Ty& z) {write(x),pc(' '),write(y),pc(' '),write(z),pc('
');}
}using namespace FastIO;
struct M
{
int a[4][4];I M(CI x=0) {memset(a,0,sizeof(a));for(RI i=0;i^4;++i) a[i][i]=x;}
I Con int* operator [] (CI x) Con {return a[x];}I int* operator [] (CI x) {return a[x];}
I M operator + (Con M& o) Con {M t;for(RI i=0;i^4;++i) for(RI j=0;j^4;++j) t[i][j]=(a[i][j]+o[i][j])%X;return t;}//矩阵加法
I M operator * (Con M& o) Con {M t;for(RI i=0;i^4;++i)//矩阵乘法
for(RI j=0;j^4;++j) for(RI k=0;k^4;++k) t[i][j]=(t[i][j]+1LL*a[i][k]*o[k][j])%X;return t;}
I bool Ex() {for(RI i=0;i^4;++i) for(RI j=0;j^4;++j) if(a[i][j]^(i==j)) return 1;return 0;}//判断是不是单位矩阵
};
class SegmentTree
{
private:
#define PT CI l=1,CI r=n,CI rt=1
#define LT l,mid,rt<<1
#define RT mid+1,r,rt<<1|1
#define PU(x) (V[x]=V[x<<1]+V[x<<1|1])
#define PD(x) (F[x].Ex()&&(T(x<<1,F[x]),T(x<<1|1,F[x]),F[x]=M(1),0))
#define T(x,v) (V[x]=V[x]*v,F[x]=F[x]*v)
M V[N<<2],F[N<<2];
public:
I void Bd(PT)//建树
{
if(F[rt]=M(1),l==r) return (void)(V[rt][0][0]=a[l],V[rt][1][1]=b[l],V[rt][2][2]=c[l],V[rt][3][3]=1);
RI mid=l+r>>1;Bd(LT),Bd(RT),PU(rt);
}
I void U(CI L,CI R,Con M& v,PT)//区间乘法
{
if(L<=l&&r<=R) return (void)T(rt,v);RI mid=l+r>>1;PD(rt);
L<=mid&&(U(L,R,v,LT),0),R>mid&&(U(L,R,v,RT),0),PU(rt);
}
I M Q(CI L,CI R,PT)//区间求和
{
if(L==l&&r==R) return V[rt];RI mid=l+r>>1;PD(rt);
if(R<=mid) return Q(L,R,LT);if(L>mid) return Q(L,R,RT);return Q(L,mid,LT)+Q(mid+1,R,RT);
}
}S;
int main()
{
RI Qt,i;for(read(n),i=1;i<=n;++i) read(a[i],b[i],c[i]);S.Bd();
RI op,x,y,v;M t;read(Qt);W(Qt--) switch(read(op,x,y),op)//对每种修改分别构造矩阵
{
case 1:(t=M(1))[1][0]=1,S.U(x,y,t);break;
case 2:(t=M(1))[2][1]=1,S.U(x,y,t);break;
case 3:(t=M(1))[0][2]=1,S.U(x,y,t);break;
case 4:read(v),(t=M(1))[3][0]=v,S.U(x,y,t);break;
case 5:read(v),(t=M(1))[1][1]=v,S.U(x,y,t);break;
case 6:read(v),(t=M(1))[3][2]=v,t[2][2]=0,S.U(x,y,t);break;
case 7:t=S.Q(x,y),writeln((0LL+t[0][0]+t[1][0]+t[2][0]+t[3][0])%X,
(0LL+t[0][1]+t[1][1]+t[2][1]+t[3][1])%X,(0LL+t[0][2]+t[1][2]+t[2][2]+t[3][2])%X);break;//输出每一列的和
}return clear(),0;
}