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;
}

浙公网安备 33010602011771号