[CSP-S2020] 函数调用 题解

题目传送门

题目大意

给出一个序列 {an}m 个函数,函数分为三种类型:

  • 类型一:将 apj 加上 vj.
  • 类型二:将整个序列乘上 vj.
  • 类型三:依次调用 cj 个函数,分别为 g1(j),g2(j),,gcj(j)

保证不出现递归调用.
最后求出调用 Q 个函数 f1,f2,fn 时数列 {an} 的每一个数字除以 998244353 所得的余数
数据范围:n,m,q105,Cj106,pj,vj104

题目解析

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

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

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

代码

#include<queue>
#include<cstdio>
#include<vector>
#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<typename _T>
#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 a<b?a:b; }
Tp void mswap(_T &a,_T &b){ _T tmp=a; a=b; b=tmp; return; }
Tp void print(_T x){ if(x<0) pc('-'),x=-x; if(x>9) 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<int> 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<int> 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<siz;i++){
v=_to[cur][i]; f[v]=f[v]*f[cur]%MOD;
in[v]--; if(!in[v]) q.push(v);
}
} return;
}
void topo2(){
int i,cur,siz,v; ll tmp; while(!q.empty()) q.pop();
for(i=0;i<=m;i++){ g[i]=0; in[i]=_to[i].size(); if(!in[i]) q.push(i); } g[0]=1;
while(!q.empty()){
cur=q.front(); q.pop(); siz=to[cur].size(); tmp=1;
for(i=siz-1;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;
}
posted @   jiangtaizhe001  阅读(131)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示