题解 P7077 【函数调用(洛谷民间数据)】

Alex_Wei

2020-11-11 18:44:57

Solution

upd on 2020.11.17:补充说明。 [题目传送门](https://www.luogu.com.cn/problem/P7077)。 不妨将题目看成:将所有数乘上 $mult$,再给某些数加上 $add_i$,其中 $mult$ 是所有被调用的 type II 函数给所有数乘的 $V_j$ 之积。 不难发现函数的调用关系是 DAG,建完图后可以一遍 dfs 求出 $mul_i$ 表示第 $i$ 个函数调用后会将所有数乘上 $mul_i$。 现在只要求出 $add_i$ 即可: 想一下,如果一个 type I 函数被调用之后,所有数又乘上了 $mult$,那么就相当于该函数产生了 $mult$ 倍的贡献,这启发我们用数组 $f_i$ 表示第 $i$ 个函数对**它所调用的**单点加法产生了 $f_i$ 倍的贡献。 可以先把每个函数在一开始调用时的产生的贡献求出,**对于 type III 函数,不进行下传递归**,最后再用拓扑排序更新真正的 $f_i$。 一开始的贡献可以**倒序处理**所有函数调用: - 如果调用 I 类函数,那么 $f_i\gets f_i+mult$。 - 如果调用 II 类函数,那么 $mult\gets mult\times V_i$。 - 如果调用 III 类函数,那么 $f_i\gets f_i+mult$,$mult\gets mult\times mul_i$。 为什么要倒序处理:一开始计算的贡献 $f_i$ 是**在调用该函数后所有数被乘了 $f_i$**,因此需要倒序处理。 最后拓扑排序时,对于每个函数: - 如果是 I 类函数,那么 $add_{P_i}\gets add_{P_i}+f_i\times V_i$。 - 如果是 III 类函数,那么**从后往前处理所有调用的函数**(原因同上): 对于每个被调用的函数 $j$,$f_j\gets f_j+f_i$。 **此时 $j$ 被调用前一个调用的函数 $pre$ 相当于产生了 $f_i\times mul_j$ 倍的贡献,因为调用 $pre$ 后调用 $j$ 将所有数乘上了 $mul_j$,也就是说调用 $pre$ 产生的单点加法贡献被放大了 $mul_j$ 倍**,此时我们将 $f_i\gets f_i\times mul_j$。 但是这样会导致原来的 $f_i$ 被覆盖,那么用一个变量代替 $f_i$ 即可。 最后每个数输出 $a_i\times mult+add_i$ 即可。 感觉可以通过差分做到线性时间区间修改,要是我就加强了( 时间,空间复杂度 $O(n+m)$。读入量较大,建议使用快读。 --- 拿样例 $1$ 举个例子: 不难求出 $mul=\{1,2,2\}$。此时 $mult=1$。 调用的函数分别为 $2,3$,那么我们倒序处理调用: 首先是 $3$,$f_3\gets f_3+mult=1$,$mult\gets mult\times mul_3=2$。 然后是 $2$,$mult\gets mult\times mul_2=4$。 此时 $mult=4$,$f=\{0,0,1\}$,度数 $deg=\{1,1,0\}$。 接下来拓扑排序,因为 $3$ 的入度为 $0$ 所以首先被压入队列。 - 对于队首函数 $3$,倒序处理所有其调用的函数: 设变量 $z\gets f_3=1$。 - 首先是 $2$: $deg_2\gets deg_2-1=0$,入度为 $0$ 被压入队列。 $f_2\gets f_2+z=1$(没有必要)。 $z\gets z\times mul_2=2$。 - 然后是 $1$: $deg_1=\gets deg_1-1=0$,入度为 $0$ 被压入队列。 $f_1\gets f_1+z=2$。 $z\gets z\times mul_1=2$(没有必要)。 - 对于队首函数 $2$,不做任何操作。 - 对于队首函数 $1$,$add_{pos_1}\gets add_{pos_1}+f_1\times V_1=0+2\times 1=2$。 最后分别输出: $a_1\times mult+add_1=1\times 4+2=6$; $a_2\times mult+add_2=2\times 4+0=8$; $a_3\times mult+add_3=3\times 4+0=12$。 --- 非考场代码: ```cpp #include <bits/stdc++.h> using namespace std; #define ll long long inline ll read(){ ll x=0,sign=0; char s=getchar(); while(s>'9'||s<'0')sign|=s=='-',s=getchar(); while(s<='9'&&s>='0')x=(x<<1)+(x<<3)+s-'0',s=getchar(); return sign?-x:x; } const int N=1e5+5; const int mod=998244353; int n,m,c,a[N],deg[N],func[N]; int tp[N],pos[N],val[N]; ll mu=1,mul[N],dp[N],add[N]; vector <int> e[N]; queue <int> q; bool vis[N]; void dfs(int id){ vis[id]=1,mul[id]=(tp[id]==2?val[id]:1); for(int it:e[id]){ if(!vis[it])dfs(it); mul[id]=mul[id]*mul[it]%mod; } } int main(){ n=read(); for(int i=1;i<=n;i++)a[i]=read(); m=read(); for(int i=1;i<=m;i++){ tp[i]=read(); if(tp[i]==1)pos[i]=read(),val[i]=read(); else if(tp[i]==2)val[i]=read(); else{ c=read(); while(c--){ int to=read(); e[i].push_back(to),deg[to]++; } } } c=read(); for(int i=1;i<=m;i++)if(!vis[i]&&!deg[i])dfs(i); for(int i=1;i<=c;i++)func[i]=read(); for(int i=c,f=func[i];i;i--,f=func[i]){ if(tp[f]==1)dp[f]=(dp[f]+mu); else if(tp[f]==2)mu=mu*val[f]%mod; else dp[f]=(dp[f]+mu),mu=mu*mul[f]%mod; } for(int i=1;i<=m;i++)if(!deg[i])q.push(i); while(!q.empty()){ int t=q.front(); q.pop(); if(tp[t]==1)add[pos[t]]=(add[pos[t]]+dp[t]*val[t])%mod; ll z=dp[t]; reverse(e[t].begin(),e[t].end()); for(int p:e[t]){ deg[p]--; if(!deg[p])q.push(p); dp[p]=(dp[p]+z)%mod,z=z*mul[p]%mod; } } for(int i=1;i<=n;i++)cout<<(a[i]*mu+add[i])%mod<<" "; return 0; } ```