[CSP-S 2020 T3] 动物园 (拓扑排序)

很难考虑每个操作的顺序,但由于操作比较简单,可以直接考虑每个操作贡献的权值

一个操作的权值可以定义为:每次这个操作执行之后后,后面所有的乘法操作的积

如果没有递归,只需要倒序枚举一次调用情况,就能知道所有的权值

对于递归的情况,显然函数之间的递归关系构成一张拓扑图,可以考虑预处理出每个操作的乘法操作之积

对于所有的函数,同样能得到一个权值,接下来的操作只需要把每个存在递归的函数不断将权值向下传给子函数

如果把最终的调用序列看做一个主函数,那么对于这个操作实际也是一样的做法

即:倒序枚举一次累积,然后乘上自己的权值下传即可

Tips: 最后加入贡献时,注意先将所有数的权值乘上全局乘法倍数,然后再依次处理每个加法操作

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
#include<bits/stdc++.h>
using namespace std;

#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)

char IO;
int rd(){
int s=0,f=0;
while(!isdigit(IO=getchar())) if(IO=='-') f=1;
do s=(s<<1)+(s<<3)+(IO^'0');
while(isdigit(IO=getchar()));
return f?-s:s;
}

const int N=1e5+10,P=998244353;

int n,m;
int A[N],T[N],H[N];
// A为原数组,T为乘法积,H为函数调用权值
int O[N],X[N],Y[N];
int B[N*11],C,L[N],R[N],ind[N];
int Q[N],QL,QR;

int main() {
//freopen("call.in","r",stdin),freopen("call.out","w",stdout);
rep(i,1,n=rd()) A[i]=rd();
m=rd()+1; // 令m+1号为主函数
rep(i,1,m) {
O[i]=i<m?rd():3,T[i]=1;
if(O[i]==1) X[i]=rd(),Y[i]=rd();
else if(O[i]==2) T[i]=rd();
else {
L[i]=C+1,R[i]=C+=rd();
rep(j,L[i],R[i]) ind[B[j]=rd()]++;
}
}
rep(i,QL=1,m) if(!ind[i]) Q[++QR]=i;
while(QL<=QR) {
int u=Q[QL++];
if(O[u]==3) rep(j,L[u],R[u]) if(--ind[B[j]]==0) Q[++QR]=B[j];
}
drep(k,m,1){
int u=Q[k];
if(O[u]==3) rep(j,L[u],R[u]) T[u]=1ll*T[u]*T[B[j]]%P;
}

rep(i,H[m]=1,n) A[i]=1ll*A[i]*T[m]%P;
rep(k,1,m) {
int u=Q[k],x=1;
drep(j,R[u],L[u]){
int v=B[j];
H[v]=(H[v]+1ll*x*H[u])%P;
x=1ll*x*T[v]%P;
}
if(O[u]==1) A[X[u]]=(A[X[u]]+1ll*H[u]*Y[u])%P;
}
rep(i,1,n) printf("%d ",A[i]); puts("");
}