P7077 [CSP-S 2020] 函数调用 题解

前言

这道题当然不能交题解啦,但是题是好题。复习TopoSort的时候就顺带写一下笔记吧。

刚开始看这道题其实蛮像线段树的,但是发现有调用函数的函数,那就变得不太像了。注意到部分分中有这样一句话:函数调用的关系构成一棵树。这启发了我们去想图论建模。然后你再读题发现:保证不会出现递归,那这张图就变成了一个有性质的DAG,往TopoSort上想,然后你就会做了。

思路

你考虑只有操作一的情况,发现不需要任何算法直接加就好了。这便成了一个小突破口。

再考虑只有操作一三的情况,发现函数调用形成了一个DAG,跑一遍拓扑。对于每一个点都做一遍操作1。

接着把操作二加上,你回想起了小学学的:

\[coe*a=a+...+a \]

你惊奇发现,操作二竟然转化成了操作一。因为乘0会出问题,我们考虑倒着从要调用它的函数回过来,并存储调用次数。再在add函数中统一乘一个系数coe,然后就没有然后了。

code

#include <bits/stdc++.h>
#define int long long
using namespace std;
namespace IO{
	const static int _M=1e6+10,_K=2e6+10 ;
	char ibuf[_K],obuf[_K],_st[_K],*P1(0),*P2(0) ;
	static int _cnt=0,_top=0 ;
	inline void flush(){return fwrite(obuf,1,_cnt,stdout),_cnt=0,void();}
	struct AutoFlush{~AutoFlush(){flush();}} flusher;
	inline char gc(){
		return (P1==P2&&(P2=(P1=ibuf)+fread(ibuf,1,_K,stdin),P1==P2)?EOF:*P1++);
	}
	template<typename T> inline void read(T&x) {
		x=0; char c; bool f=0;
		do{c=gc() ;if(c=='-') f=1 ;}while(c<48) ;
		do{x=(x<<3)+(x<<1)+(c^48); c=gc() ;}while(c>=48) ;
		return (f?x=~x+1,void():void()),void();
	}
	template<typename T> inline void readmod(T&x,int mod) {
		x=0; char c ;
		do{c=gc() ;} while(c<48) ;
		do{x=((x<<3)+(x<<1)+(c^48))%mod; c=gc() ;}while(c>=48) ;
		return ;
	}
	inline void pc(char c) {obuf[_cnt++]=c; if(_cnt>=_M) flush(); return ;}
	inline void ps(string s) {for(auto c:s) obuf[_cnt++]=c; if(_cnt>=_M) flush(); return ;}
	template<typename T>inline void write(T x,char c='\0') {
		if(x<0) {pc('-'); x=-x;}
		do{_st[++_top]=(x%10)|48 ;}while(x/=10) ;
		while(_top) pc(_st[_top--]) ;
		return (c?pc(c):void()),void() ;
	}
}
using namespace IO ;
bool M1 ;
const int N=1e6+10 ,mod=998244353;
int a[N] ,n,m,opt[N],mul[N],p[N],add[N],cnt[N],du[N],rdu[N];
/*
opt[i] -> 第i次操作是什么 方便离线处理
mul[i] -> 第i次乘的系数
p[i] -> 如题 下标
add[i] -> 加数
cnt[i] -> 一共调用了多少次函数
du[i] -> 正图的入度
rdu[i] -> 反图的入度
a[i] -> 初始值
*/
vector<int> g[N],rg[N] ;
bool M2 ;

void topo_mul() {
	queue<int> q ;
	for(int i=0;i<=m;i++) if(rdu[i]==0) q.emplace(i) ;
	while(!q.empty()) {
		int u=q.front() ; q.pop() ;
		for(auto v:rg[u]) {
			(mul[v]*=mul[u])%=mod;
			if(--rdu[v]==0) q.push(v) ;
		}
	}
}

void topo_add() {
	queue<int> q; 
	for(int i=0;i<=m;i++) if(du[i]==0) q.emplace(i) ;
	while(!q.empty()) {
		int u=q.front() ; q.pop() ;
		int coe=1 ;
		for(int i=g[u].size()-1;i>=0;i--) {
			int v=g[u][i];
			(cnt[v]+=coe*cnt[u])%=mod ;
			(coe*=mul[v])%=mod ;
			if(--du[v]==0) q.push(v) ;
		}
	} 
}

signed main() {
	read(n) ;
	for(int i=1;i<=n;i++) read(a[i]) ;
	read(m) ;
	for(int i=1;i<=m;i++) {
		read(opt[i]) ;
		mul[i]=1 ;
		if(opt[i]==1) {read(p[i]); read(add[i]) ;}
		else if(opt[i]==2) read(mul[i]) ;
		else {
			int tot ;
			read(tot); 
			for(int j=1,v;j<=tot;j++) {
				read(v); 
				g[i].push_back(v) ; rg[v].push_back(i) ;
				du[v]++; rdu[i]++ ;
			}
		}
	}
	
	int T; read(T) ; 
	mul[0]=1 ;cnt[0]=1 ;
	while(T--) {
		int v; read(v) ;
		g[0].push_back(v); rg[v].push_back(0);
		rdu[0]++; du[v]++;
	}
	
	topo_mul() ; topo_add() ;
	
	for(int i=1;i<=n;i++) (a[i]*=mul[0])%=mod ;
	for(int i=1;i<=m;i++) if(opt[i]==1) (a[p[i]]+=cnt[i]*add[i])%=mod;
	
	for(int i=1;i<=n;i++) write(a[i],' ') ;
	cerr<<"\nused memroy="<<abs(&M2-&M1)/1024/1024<<"MB\n" ;
	
	return 0;
}

posted @ 2026-07-24 21:00  Emumumu  阅读(13)  评论(0)    收藏  举报