[题解]P7077 [CSP-S2020] 函数调用
题意简述
给定一个长度为\(n\)的序列\(a_1,a_2,\dots,a_n\),给定\(m\)个函数,每个函数可能是下面\(3\)种类型,用\(T_x\)表示函数\(x\)的类型:
- \(T_x =1\),对下标\(p\)增加\(v\)。
- \(T_x =2\),对所有元素乘\(v\)。
- \(T_x =3\),由若干类型\(1\)和类型\(2\)组成的复合函数,按顺序执行。
给定初始序列和每个函数的定义,最后给定一个调用序列,你需要按顺序调用完该序列所有函数后输出序列。
思路分析
我们把函数看作节点,调用关系看作有向边(调用连向被调用),因为没有递归调用,所以这是一个DAG(有向无环图)。
由于乘法是区间的,所以最终每个\(a_i\)都可以表示为\(a_i\times k+b_i\)。其中\(k\)就是函数序列中每个函数的\(mul\)值之积,其中\(mul_i\)的定义如下:
- \(i\)是类型\(1\)函数:\(mul_i=1\)。
- \(i\)是类型\(2\)函数:\(mul_i=v\)。
- \(i\)是类型\(3\)函数:\(mul_i=\prod\limits_{i\rightarrow j} mul_j\)。
这样\(k\)就计算出来了。
我们接下来要计算\(b_i\),那么需要知道每个\(1\)类函数被执行了多少次,我们记第\(1\)类函数\(x\)被执行的次数为\(cnt_x\),那么\(b_i=\sum\limits_{T_{_j}=1,p_{_j}=i}v_j\times cnt_j\)。
先考虑没有类型\(2\)函数的情况,我们只需要对于调用的每个函数\(i\),将\(cnt_i \leftarrow cnt_i +1\),然后再DAG上递推,把父节点的\(cnt\)推给子节点即可。最后对于每个叶子结点(类型\(1,2\)的函数)中类型为\(1\)的函数\(i\),将\(cnt_i\times v_i\)加给\(p_i\)即可。
如果有类型\(2\)函数,我们推给子节点的\(cnt\)可能会乘上一个系数,假设我们当前遍历到节点\(u\),有\(s\)个子节点,对于第\(i\)个子节点\(son_i\),\(u\)对\(son_i\)的贡献是\(cnt_u\times cur\),其中\(cur=\prod\limits_{j=i+1}^{s} mul_i\)。这是因为后面函数的“乘”操作会影响到前面的函数。所以我们可以倒序遍历子节点,累乘贡献即可。
相应地,主函数中为\(sum\)赋初值也需要累乘贡献。
也可以这样思考:序列初始为\(0\),第\(i\)位增加\(a_i\)作为\(n\)个函数额外放在其他函数前执行。这样输出就不用管\(a_i\times k\)了,只要输出\(b_i\)即可。
Code
点击查看代码
#include<bits/stdc++.h>
#define N 100010
#define mod 998244353
#define int long long
using namespace std;
int n,m,q,a[N],deg[N],fun[N];
int p[N],v[N];//存储叶子节点(操作1,2)的信息
int ord[N],tn;//ord[i]表示拓扑序为i的节点,tn为计数器
int mul[N];//mul[i]表示函数i要乘多少
int cnt[N];//cnt[i]表示函数i要执行多少次
vector<int> G[N];
queue<int> que;
void topo(){//预处理出拓扑序,方便转移
for(int i=1;i<=m;i++)
if(!deg[i]) que.push(i);
while(!que.empty()){
int u=que.front();
que.pop(),ord[++tn]=u;
for(int i:G[u]){
deg[i]--;
if(!deg[i]) que.push(i);
}
}
}
void getmul(){//自下而上计算节点的mul
for(int i=m;i>=1;i--){
int u=ord[i];
for(int j:G[u]) mul[u]=mul[u]*mul[j]%mod;
}
}
void getsum(){//自上而下计算节点的sum
for(int i=1;i<=m;i++){
int u=ord[i],cur=cnt[u];
for(int j:G[u])
cnt[j]=(cnt[j]+cur%mod)%mod,
cur=cur*mul[j]%mod;
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr),cout.tie(nullptr);
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
cin>>m;
for(int i=1;i<=m;i++){
int op;
cin>>op;
if(op==1){
cin>>p[i]>>v[i];
mul[i]=1;
}else if(op==2){
cin>>v[i];
mul[i]=v[i];
}else{
int c,v;
cin>>c;
mul[i]=1;
while(c--){
cin>>v;
G[i].emplace_back(v);
deg[v]++;
}
}
}
for(int i=1;i<=m;i++) reverse(G[i].begin(),G[i].end());//倒序遍历
topo();
getmul();
cin>>q;
int cur=1;
for(int i=1;i<=q;i++) cin>>fun[i];
for(int i=q;i>=1;i--){
int u=fun[i];
cnt[u]=(cnt[u]+cur)%mod;
cur=cur*mul[u]%mod;
}
getsum();
for(int i=1;i<=n;i++) a[i]=a[i]*cur%mod;
for(int i=1;i<=m;i++){
if(p[i]) a[p[i]]=(a[p[i]]+v[i]*cnt[i]%mod)%mod;
}
for(int i=1;i<=n;i++) cout<<a[i]<<" ";
return 0;
}
[题解]P7077 [CSP-S2020] 函数调用的更多相关文章
- 2019 CSP J/S第2轮 视频与题解
CSP入门组和提高组第二轮题解 转自网络
- P7077 函数调用
我好蠢啊... 考试的时候不会写,现在看了这么多篇题解还是似懂非懂,所以决定写一下草稿... 草稿 和 题解 就是首先,题目保证了函数不会间接的调用其本身,所以可以直接知道这是一个 \(\text{D ...
- 上午小测3 T1 括号序列 && luogu P5658 [CSP/S 2019 D1T2] 括号树 题解
前 言: 一直很想写这道括号树..毕竟是在去年折磨了我4个小时的题.... 上午小测3 T1 括号序列 前言: 原来这题是个dp啊...这几天出了好几道dp,我都没看出来,我竟然折磨菜. 考试的时候先 ...
- [CSP模拟测试43、44]题解
状态极差的两场.感觉现在自己的思维方式很是有问题. (但愿今天考试开始的一刻我不会看到H I J) A 考场上打了最短路+贪心,水了60. 然而正解其实比那30分贪心好想多了. 进行n次乘法后的结果一 ...
- 题解 nflsoj489 【六校联合训练 CSP #15】小D与随机
题目链接 考虑枚举好点的集合.此时要考虑的问题是如何填入\(1\sim n\)这些数使得恰好我们枚举到的这些点是好点,即:求出有多少种合法的填数方案. \(1\)号点一定是好点.那么除\(1\)号点外 ...
- C++对象模型的那些事儿之六:成员函数调用方式
前言 C++的成员函数分为静态函数.非静态函数和虚函数三种,在本系列文章中,多处提到static和non-static不影响对象占用的内存,而虚函数需要引入虚指针,所以需要调整对象的内存布局.既然已经 ...
- CSP应用开发-CryptAPI函数库介绍
基本加密函数为开发加密应用程序提供了足够灵活的空间.所有CSP的通讯都是通过这些函数.一个CSP是实现所有加密操作的独立模块.在每一个应用程序中至少需要提供一个CSP来完成所需的加密操作.如果使用多于 ...
- 算法(第四版)C# 习题题解——2.3
写在前面 整个项目都托管在了 Github 上:https://github.com/ikesnowy/Algorithms-4th-Edition-in-Csharp 查找更为方便的版本见:http ...
- CCF CSP 201703-3 Markdown
CCF计算机职业资格认证考试题解系列文章为meelo原创,请务必以链接形式注明本文地址 CCF CSP 201703-3 Markdown 问题描述 Markdown 是一种很流行的轻量级标记语言(l ...
- CCF计算机职业资格认证考试题解
CCF计算机职业资格认证考试题解系列文章为meelo原创,请务必以链接形式注明本文地址 CCF计算机职业资格认证考试题解 CCF计算机软件能力认证(简称CCF CSP认证)是CCF计算机职业资格认证系 ...
随机推荐
- MySQL 数字保留两位小数
1.ROUND(x,d) 用于数据的四舍五入,ROUND(x)其实就是ROUND(x,0),也就是默认d为0:这里有个值得注意的地方是,d可以是负数,这时是指定小数点左边的d位整数位为0,同时小数位均 ...
- mysql安装配置启动
1. 安装 & 配置 & 启动 MySQL现在的版本主要分为: 5.x 版本,现在互联网企业中的主流版本,包括:头条.美图.百度.腾讯等互联网公司主流的版本. 8.x 版本,新增了一些 ...
- AI大模型应用开发入门-LangChain开发聊天机器人ChatBot
在大模型应用开发中,状态管理 和 对话追踪 是不可忽视的重要能力,尤其在需要保存上下文.重放对话或进行异步处理时尤为关键. 今天我们来演示如何用 LangChain + OpenAI 的 GPT 模型 ...
- 在工具类静态方法调用@Autowired注入的bean方法
今天在搞一个工具类的时候,需要在工具类的静态方法中调用mapper的方法插入数据,但是,用spring的@Autowired注入bean后,测试一跑,报空指针异常. 解决方案如下: 1.对工具类使用@ ...
- luffy
Luffy 本项目使用Vue+Vite+Django+DRF+Elementui-Plus编写 项目需求介绍 # 线上销售课程的 -商城 -知识付费类 # 需求 -首页功能 -轮播图接口 -推荐课程接 ...
- Jfinal启动报错-文件找不到
- pdf工具类之合并pdf文件
实现思路:根据文件集合中第一个pdf文件新建一个pdf文件对象和文件流,将此pdf文件打开,然后循环文件及和将所有的文件按章集合顺序添加到创建的这个文件中 这样生成的pdf文件的每一个部分,会和原pd ...
- 前端开发系列134-进阶篇之脚手架Yue-cli的实现03-download功能
这是系列文章前端脚手架实现的第三篇,本文核心解决当我们通过模板来初始化项目时如何把选定版本的模板文件下载到对应文件夹中的问题. 当我们在拉取模板文件的时候,存在两种情况,一种是直接把初始化项目的模板文 ...
- matlab 求解常微分方程
简介 高数中关于求解常微分方程,我一直记得是公式 code clc, clear syms y(x) % 定义符号常量 dsolve(x^2 + y + (x -2 * y) * diff(y) == ...
- 抛开官方库,手撸一个轻量级 MCP 服务端
大家好!在昨天的文章 <官方文档没告诉你的:通过抓包,深入揭秘MCP协议底层通信> 中,我们通过Fiddler工具,像侦探一样,一步步揭开了MCP(Model Context Protoc ...