[CSP-S2020] 函数调用 题解


题目传送门

题目大意

给出一个序列 \(\{a_n\}\)\(m\) 个函数,函数分为三种类型:

  • 类型一:将 \(a_{p_j}\) 加上 \(v_j\).
  • 类型二:将整个序列乘上 \(v_j\).
  • 类型三:依次调用 \(c_j\) 个函数,分别为 \(g_1^{(j)},g_2^{(j)},\dots,g_{c_j}^{(j)}\)

保证不出现递归调用.
最后求出调用 \(Q\) 个函数 \(f_1,f_2\dots,f_n\) 时数列 \(\{a_n\}\) 的每一个数字除以 \(998244353\) 所得的余数
数据范围:\(n,m,q\le 10^5,\sum C_j\le 10^6,p_j,v_j\le 10^4\)

题目解析

一眼数据结构,其实是图论题。

显然最后的这 \(q\) 个函数也可以看成一个类型三的函数.
如果直接模拟调用显然直接 TLE.

如果不考虑类型二的函数,那么显然可以通过拓扑排序来算出每个函数被调用了几次,最后累加.
现在需要考虑类型二的函数.
我们先考虑这个数列的第 \(i\) 位。如果第 \(j\) 个函数能将这一位加上 \(v\),并且被调用了 \(r\) 次,那么这时这一位的数字为 \(a_i+v\times r\).
这时我们把这个数字乘上 \(k\),得到 \((a_i+v\times r)\times k\),其实就是 \(a_i\times k + v\times r\times k\),这就相当于把原序列乘上了 \(k\),并且把之前调用的函数都继续调用了 \(k\) 次.
所以我们只需要把这个函数调用以后整个序列被乘上几次预处理出来就可以拓扑了.注意在拓扑的时候要倒着枚举调用的函数,因为类型二的函数只会对调用之前的函数产生影响.

代码

#include
#include
#include
#define db double
#define gc getchar
#define pc putchar
#define U unsigned
#define ll long long
#define ld long double
#define ull unsigned long long
#define Tp template
#define Me(a,b) memset(a,b,sizeof(a))
Tp _T mabs(_T a){ return a>0?a:-a; }
Tp _T mmax(_T a,_T b){ return a>b?a:b; }
Tp _T mmin(_T a,_T b){ return a9) print(x/10); pc((x%10)+48); return; }
#define EPS (1e-7)
#define INF (0x7fffffff)
#define LL_INF (0x7fffffffffffffff)
#define maxn 100039
#define maxm
#define MOD 998244353
#define Type long long
#ifndef ONLINE_JUDGE
//#define debug
#endif
using namespace std;
Type read(){
	char c=gc(); Type s=0; int flag=0;
	while((c<'0'||c>'9')&&c!='-') c=gc(); if(c=='-') c=gc(),flag=1;
	while('0'<=c&&c<='9'){ s=(s<<1)+(s<<3)+(c^48); c=gc(); }
	if(flag) return -s; return s;
}
int n,m,in[maxn];
struct JTZ{ int t,p,v; }fun[maxn];
ll a[maxn],f[maxn],g[maxn];//f times g adds
vector to[maxn],_to[maxn];
void init(){
	int i,j,v; n=read(); for(i=1;i<=n;i++) a[i]=read(); m=read();
	for(i=1;i<=m;i++){
		fun[i].t=read();
		if(fun[i].t==1) f[i]=1,fun[i].p=read(),fun[i].v=read();
		else if(fun[i].t==2) f[i]=read();
		else{
			fun[i].p=read(); f[i]=1;
			for(j=1;j<=fun[i].p;j++){ v=read(); to[i].push_back(v); _to[v].push_back(i); }
		}
	}
	fun[0].t=3; fun[0].p=read(); f[0]=1;
	for(i=1;i<=fun[0].p;i++){ v=read(); to[0].push_back(v); _to[v].push_back(0); } return;
} queue q;
void topo1(){
	int i,cur,siz,v;
	for(i=0;i<=m;i++){ in[i]=to[i].size(); if(!in[i]) q.push(i); }
	while(!q.empty()){
		cur=q.front(); q.pop(); siz=_to[cur].size();
		for(i=0;i=0;i--){
			v=to[cur][i]; g[v]=(g[v]+g[cur]*tmp%MOD)%MOD;
			tmp=tmp*f[v]%MOD; in[v]--; if(!in[v]) q.push(v);
		}
	} return;
}
void js(){
	int i; for(i=1;i<=n;i++) a[i]=a[i]*f[0]%MOD;
	for(i=1;i<=m;i++) if(fun[i].t==1) a[fun[i].p]=(a[fun[i].p]+g[i]*fun[i].v%MOD)%MOD;
	for(i=1;i<=n;i++) print(a[i]),pc(' '); return;
}
int main(){
	#ifndef ONLINE_JUDGE
	freopen("call3.in","r",stdin);
	freopen("1.out","w",stdout);
	#endif
	init(); topo1(); topo2(); js(); return 0;
}