【THUSC2017】大魔法师 题解

题目大意

  维护三个长度为 $n$ 的序列 $A,B,C$,支持以下 7 种操作:(操作数为 $m$)

  • $1\ l\ r$:对 $[l,r]$,$A_i \gets A_i+B_i$;
  • $2\ l\ r$:对 $[l,r]$,$B_i \gets B_i+C_i$;
  • $3\ l\ r$:对 $[l,r]$,$C_i \gets C_i+A_i$;
  • $4\ l\ r\ v$:对 $[l,r]$,$A_i \gets A_i+v$;
  • $5\ l\ r\ v$:对 $[l,r]$,$B_i \gets B_i \cdot v$;
  • $6\ l\ r\ v$:对 $[l,r]$,$C_i \gets v$;
  • $7\ l\ r$:求 $\sum_{i=l}^r A_i,\ \sum_{i=l}^r B_i,\ \sum_{i=l}^r C_i$,在模 $998244353$ 意义下。

  $n,m \leq 2.5 \times 10^5,\ 0 \leq A_i,B_i,C_i < 998244353$
  5s

题解

  开始补高中的眼泪了,在 thu 门外哭哭的日子

  这种序列互加的要想到矩阵运算。
  比如令一个向量 $\vec{x_i}$ 等于 $[A_i,B_i,C_i]$,那么 $\vec{x_i} \cdot \begin{bmatrix} 1 & 0 & 0 \\ 1 & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix}$ 就实现了第一种操作。
  其他操作同理。
  对于 4~6 操作,再把向量加一维就可以了。

  那么就用线段树维护区间向量和,上面的 7 个操作相当于给区间打矩阵乘法标记。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

typedef long long LL;

const int maxn=250005;
const LL mo=998244353;

struct ARR{
LL n[4][4];//={{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}};
ARR() {n[0][0]=n[1][1]=n[2][2]=n[3][3]=1;}
} rea,I;
struct VEC{
LL n[4];
} rev;
ARR operator * (const ARR &a,const ARR &b)
{
fo(i,0,3)
fo(j,0,3)
rea.n[i][j]=(a.n[i][0]*b.n[0][j]+a.n[i][1]*b.n[1][j]
+a.n[i][2]*b.n[2][j]+a.n[i][3]*b.n[3][j])%mo;
return rea;
}
VEC operator * (const VEC &a,const ARR &b)
{
fo(i,0,3) rev.n[i]=(a.n[0]*b.n[0][i]+a.n[1]*b.n[1][i]+a.n[2]*b.n[2][i]+a.n[3]*b.n[3][i])%mo;
return rev;
}
VEC operator + (const VEC &a,const VEC &b)
{
return (VEC){(a.n[0]+b.n[0])%mo,(a.n[1]+b.n[1])%mo,(a.n[2]+b.n[2])%mo,(a.n[3]+b.n[3])%mo};
}

int n,m,a[maxn],b[maxn],c[maxn];

void ReadInt(int &data)
{
data=0;
char ch=getchar();
while (ch<'0' || ch>'9') ch=getchar();
do{
data=(data<<3)+(data<<1)+ch-'0';
ch=getchar();
} while (ch>='0' && ch<='9');
}

VEC tr[4*maxn],ans;
ARR tag[4*maxn],C;
bool bz[4*maxn];
int x,y;
void tr_js(int k,int l,int r)
{
if (l==r)
{
tr[k]=(VEC){{a[l],b[l],c[l],1}};
return;
}
int t=k<<1, mid=(l+r)>>1;
tr_js(t,l,mid), tr_js(t+1,mid+1,r);
tr[k]=tr[t]+tr[t+1];
}
void update(int k,int t)
{
if (!bz[k]) return;
tr[t]=tr[t]*tag[k], tr[t+1]=tr[t+1]*tag[k];
tag[t]=tag[t]*tag[k], tag[t+1]=tag[t+1]*tag[k];
bz[t]=bz[t+1]=1;
tag[k]=I;
bz[k]=0;
}
void tr_xg(int k,int l,int r)
{
if (x<=l && r<=y)
{
tr[k]=tr[k]*C;
tag[k]=tag[k]*C;
bz[k]=1;
return;
}
int t=k<<1, mid=(l+r)>>1;
update(k,t);
if (x<=mid) tr_xg(t,l,mid);
if (mid<y) tr_xg(t+1,mid+1,r);
tr[k]=tr[t]+tr[t+1];
}
void tr_cx(int k,int l,int r)
{
if (x<=l && r<=y)
{
ans=ans+tr[k];
return;
}
int t=k<<1, mid=(l+r)>>1;
update(k,t);
if (x<=mid) tr_cx(t,l,mid);
if (mid<y) tr_cx(t+1,mid+1,r);
}

int main()
{
ReadInt(n);
fo(i,1,n) ReadInt(a[i]), ReadInt(b[i]), ReadInt(c[i]);

tr_js(1,1,n);

ReadInt(m);
while (m--)
{
int opt,v;
ReadInt(opt), ReadInt(x), ReadInt(y);
if (opt>=4 && opt<=6) ReadInt(v);
C=I;
switch (opt) {
case 1: {C.n[1][0]=1; tr_xg(1,1,n); break;}
case 2: {C.n[2][1]=1; tr_xg(1,1,n); break;}
case 3: {C.n[0][2]=1; tr_xg(1,1,n); break;}
case 4: {C.n[3][0]=v; tr_xg(1,1,n); break;}
case 5: {C.n[1][1]=v; tr_xg(1,1,n); break;}
case 6: {C.n[2][2]=0, C.n[3][2]=v; tr_xg(1,1,n); break;}
case 7: {
ans=(VEC){{0,0,0,0}};
tr_cx(1,1,n);
printf("%lld %lld %lld\n",ans.n[0],ans.n[1],ans.n[2]);
}
}
}
}