题解 P7077 【函数调用】

囧仙

2020-11-11 12:46:45

Solution

## 题目大意 - $n$ 个数,初始为 $a_i$ 。现在有 $m$ 个操作,共三种: - $\verb!1 p v!$ 给 $a_p$ 加上 $v$ 。 - $\verb!2 v !$ 给所有数字乘上 $v$ 。 - $\verb!3 c g1 g2 ... gc!$ 依次执行操作 $g_1,g_2,\cdots g_c$ 。 - 现在依次执行 $f_1,f_2,\cdots f_q$ 操作,输出最终结果。结果对 $998244353$ 取模。 ## 题解 显然,数列的初始化就相当于在所有操作前依次执行 $\verb!1 i ai!$ 。 考虑没有操作 $3$ 的情况。现在我们考虑一个初始为 $0$ ,长度为 $3$ 的序列。操作序列如下: $$\verb!(1 1 1),(2 2),(1 3 2),(2 3),(1,2,5)!$$ 它会依次执行以下操作: - $1.$ 给 $a_1$ 加上 $1$ 。数列变为 $\text{1 0 0}$ 。 - $2.$ 所有 $a_i$ 乘 $2$ 。数列变为 $\text{2 0 0}$ 。 - $3.$ 给 $a_3$ 加上 $2$ 。数列变为 $\text{2 0 2}$ 。 - $4.$ 所有 $a_i$ 乘 $3$ 。数列变为 $\text{6 0 6}$ 。 - $5.$ 给 $a_2$ 加上 $5$ 。数列变为 $\text{6 5 6}$ 。 现在考虑每个操作对前面操作的影响。(下面“第 $i$ 个操作”表示操作的序号)。我们发现, 第二个操作相当于使第一个操作一共执行两次;第四个操作相当于使“执行了两次的第一个操作”又执行了三次,因此第一个操作一共执行了六次。 我们能够发现,第二种操作的用处是,使之前第一种操作的执行次数变成 $v_i$ 倍。 - $a_1=(p_1\times v_2\times v_3)=6,a_3=(p_3\times v_3)$ 。 那么考虑倒过来处理问题。比如依然是上面这个例子。我们设 $x$ 表示第二种操作的**叠乘系数**,初始为 $1$ 。 - $5.$ 执行 $x=1$ 次让 $a_2$ 加上 $p_5=5$ 。数列变为 $\text{0 5 0}$ 。 - $4.$ 让叠乘系数 $x$ 乘上 $3$ ,此时 $x=3$ 。 - $3.$ 执行 $x=3$ 次让 $a_3$ 加上 $p_3=2$ 。数列变为 $\text{0 5 6}$ 。 - $2.$ 让叠乘系数 $x$ 乘上 $2$ ,此时 $x=6$ 。 - $1.$ 执行 $x=6$ 次让 $a_1$ 加上 $p_1=1$ 。数列变为 $\text{6 5 6}$ 。 此时,没有操作 $3$ 的子情况被我们解决了。现在考虑有操作 $3$ 的情况。 将每个操作看成一个节点,每个操作 $3$ 向子操作连边。此外,题面的最后一步会从 $1$ 到 $q$ 执行 $f_i$ 。我们可以用一个特殊的第 $0$ 个操作向 $f_i$ 连边来简化问题。 由于题面保证了这张图有向无环(没有递归),所以我们能用 $dp$ 计算出执行完每个操作后叠乘系数将会乘上的值 $T_i$ 。我们假设每个操作会被执行 $C_i$ 次。 考虑什么情况下 $C_i$ 会被改变。显然,当操作 $i$ 之后还有操作需要执行时, $C_i$ 的值会乘上它之后操作的 $\prod T_j$ 。但我们能发现,当他后面没有操作时,这个操作就能直接执行 $C_i$ 次。也就是说,当他的入度为 $0$ ,就可以更新并删除这个节点了。 有个细节。有些操作可能根本不会被执行,这时候我们要忽略它们。防止它们的出边对拓扑造成影响。 ## 参考代码 ```cpp #include<bits/stdc++.h> #define up(l,r,i) for(int i=l,END##i=r;i<=END##i;++i) #define dn(r,l,i) for(int i=r,END##i=l;i>=END##i;--i) using namespace std; typedef long long i64; const int MOD =998244353; const int MAXN=1e5+3,MAXM=2e6+1e5+3; int H[MAXM],R[MAXM],N[MAXM],O[MAXM],tot; int qread(){ int w=1,c,ret; while((c=getchar())> '9'||c< '0') w=(c=='-'?-1:1); ret=c-'0'; while((c=getchar())>='0'&&c<='9') ret=ret*10+c-'0'; return ret*w; } void add(int u,int v){ R[++tot]=v,N[tot]=H[u],H[u]=tot,++O[v]; } int I[MAXM],P[MAXM],V[MAXM],T[MAXM],F[MAXM]; int clc(int u){ if(T[u]!=-1) return T[u]; T[u]=1; for(int i=H[u];i;i=N[i]) T[u]=1ll*T[u]*clc(R[i])%MOD; return T[u]; } int A[MAXM],Q[MAXM],C[MAXM],n,m,q,r; int main(){ n=qread(); up(1,n,i) add(0,i),P[i]=i,V[i]=qread(),T[i]=1,I[i]=1; m=qread(); up(n+1,n+m,i) switch(I[i]=qread()){ case 1: P[i]=qread(),V[i]=qread(),T[i]=1; break; case 2: V[i]=qread(),T[i]=V[i]; break; case 3: up(1,qread(),j) add(i,qread()+n); T[i]=-1; } up(1,n+m,i) clc(i); q=qread(); up(1,q,i) add(0,n+qread()); up(0,n+m,i) if(O[i]==0) Q[++r]=i; I[0]=3,C[0]=1; while(r>0){ int u=Q[r],x=C[u]; --r; if(I[u]==3) for(int i=H[u];i;i=N[i]){ C[R[i]]=(x+C[R[i]])%MOD,--O[R[i]]; if(O[R[i]]==0) Q[++r]=R[i]; x=1ll*x*T[R[i]]%MOD; } else if(I[u]==1) A[P[u]]=(1ll*V[u]*x+A[P[u]])%MOD; } up(1,n,i) printf("%d ",A[i]); return 0; } ```