函数调用:个人非常喜欢的一道拓扑题。

转化

这题一共有三种操作,不太好搞。而第一个函数看起来就比较可做,第三个函数显然就是让你拓扑转移,于是我们考虑第二个操作怎么处理。

当我们进行一个操作一后,假设当前节点的增量为 \(ad\)。此时如果再进行一次操作二,全局乘上 \(mul\),那么该节点最终的增量为 \(ad\times mul\)。根据乘法的定义,我们可以把这个增量转化为对该节点进行 \(mul\) 次操作一。

这就启发了我们考虑每个操作一会被执行多少次,显然是这个操作后面的操作的后缀积。

于是这题最关键的一步转化就做完了。

拓扑

因为操作三是要依次执行操作,也就是说它的子操作执行时,乘上的操作次数可能不相同。

因此,我们可以先预处理出每个操作完成之后会给全局乘上什么数,以便后面的计算。这可以通过倒着拓扑一遍处理。

接下来就要转移函数执行次数了,我们定义 \(dp_i\) 表示该操作要被执行的次数。

显然,我们可以倒着枚举执行的操作,维护该操作内部的后缀积 \(nmul\),进行如下转移:

\[dp_v \gets dp_v+dp_u \times nmul
\]

这就保证了前面的操作一定会算上后面函数的乘积了。

那么该如何初始化呢?同样是倒序枚举它给定的操作,维护一个后缀积,处理到某个操作的初始值时,其初始值就是当前的后缀积。

最后统计答案是容易的,因为只剩下操作一了,只要我们依次累计上答案即可。注意原来的值要乘上所有操作的全局积。

时间复杂度 \(O(m)\)。

实现

这题码量较大,再次梳理一遍代码过程:

  1. 倒着拓扑一遍,计算出每个函数操作之后给全局乘了多少。
  2. 倒着操作所有函数调用,实时维护一个后缀乘积,然后给各函数打标记。
  3. 顺着拓扑一遍,对于第三种函数,我们倒着枚举它的出边,然后进行转移,转移之后乘上对应节点的全局乘贡献。

代码

以上三个步骤在代码中被拆分成了三个函数来实现。

注意所有时候都要取模。

#include <bits/stdc++.h>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pi;
const ll mod=998244353;
int n,m,rd[100005],frd[100005],q,opf[100005];
ll a[100005],mul[100005],dp[100005],allmul,ad[100005];
struct node{
int tp,p;
ll v;
}f[100005];
vector<int>g[100005],fg[100005];
void topo1()
{
queue<int>q;
for(int i=1;i<=m;i++)
{
if(frd[i]==0)q.push(i);
if(f[i].tp==2)mul[i]=f[i].v;
else mul[i]=1;
}
while(!q.empty())
{
int u=q.front();
q.pop();
for(auto v:fg[u])
{
mul[v]=(mul[v]*mul[u])%mod;
frd[v]--;
if(frd[v]==0)q.push(v);
}
}
}
void init()
{
ll nmul=1;
for(int i=q;i>=1;i--)
{
dp[opf[i]]=(dp[opf[i]]+nmul)%mod;
nmul=(nmul*mul[opf[i]])%mod;
}
allmul=nmul;
}
void topo2()
{
queue<int>q;
for(int i=1;i<=m;i++)if(rd[i]==0)q.push(i);
ll nmul=1;
while(!q.empty())
{
int u=q.front();
q.pop();
nmul=1;
for(int i=g[u].size()-1;i>=0;i--)
{
int v=g[u][i];
dp[v]=(dp[v]+dp[u]*nmul%mod)%mod;
nmul=(nmul*mul[v])%mod;
rd[v]--;
if(rd[v]==0)q.push(v);
}
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
cin>>m;
for(int i=1;i<=m;i++)
{
int tp,p,v;
cin>>tp;
if(tp==1)
{
cin>>p>>v;
f[i]={1,p,v};
}
else if(tp==2)
{
cin>>v;
f[i]={2,0,v};
}
else
{
cin>>p;
for(int j=1;j<=p;j++)
{
int to;
cin>>to;
g[i].push_back(to);
rd[to]++;
fg[to].push_back(i);
frd[i]++;
}
f[i]={3,p,0};
}
}
cin>>q;
for(int i=1;i<=q;i++)cin>>opf[i];
topo1();
init();
topo2();
for(int i=1;i<=m;i++)if(f[i].tp==1)ad[f[i].p]=(ad[f[i].p]+dp[i]*f[i].v%mod)%mod;
for(int i=1;i<=n;i++)cout<<(allmul*a[i]%mod+ad[i])%mod<<" ";
return 0;
}

Luogu P7077 CSP-S2020 函数调用 题解 [ 蓝 ] [ 拓扑排序 ] [ 动态规划 ] [ 数学 ]的更多相关文章

  1. 【BZOJ1471】不相交路径 题解(拓扑排序+动态规划+容斥原理)

    题目描述 在有向无环图上给你两个起点和终点分别为$a,b,c,d$.问有几种路径方案使得能从$a$走到$b$的同时能从$c$走到$d$,且两个路径没有交点. $1\leq n\leq 200,1\le ...

  2. 洛谷 P3244 / loj 2115 [HNOI2015] 落忆枫音 题解【拓扑排序】【组合】【逆元】

    组合计数的一道好题.什么非主流题目 题目背景 (背景冗长请到题目页面查看) 题目描述 不妨假设枫叶上有 \(n​\) 个穴位,穴位的编号为 \(1\sim n​\).有若干条有向的脉络连接着这些穴位. ...

  3. bzoj 1093 最大半连通子图 - Tarjan - 拓扑排序 - 动态规划

    一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:?u,v∈V,满足u→v或v→u,即对于图中任意两点u,v,存在一条u到v的有向路径或者从v到u的有向路径.若G'=(V ...

  4. Luogu3953 NOIP2017逛公园(最短路+拓扑排序+动态规划)

    跑一遍dij根据最短路DAG进行拓扑排序,按拓扑序dp即可.wa了三发感觉非常凉. #include<iostream> #include<cstdio> #include&l ...

  5. [luogu]P4316 绿豆蛙的归宿(拓扑排序,期望)

    P4316 绿豆蛙的归宿 题目背景 随着新版百度空间的上线,Blog宠物绿豆蛙完成了它的使命,去寻找它新的归宿. 题目描述 给出一个有向无环图,起点为1终点为N,每条边都有一个长度,并且从起点出发能够 ...

  6. ACM/ICPC 之 拓扑排序范例(POJ1094-POJ2585)

    两道拓扑排序问题的范例,用拓扑排序解决的实质是一个单向关系问题 POJ1094(ZOJ1060)-Sortng It All Out 题意简单,但需要考虑的地方很多,因此很容易将code写繁琐了,会给 ...

  7. Codeforces Round #292 (Div. 1) B. Drazil and Tiles 拓扑排序

    B. Drazil and Tiles 题目连接: http://codeforces.com/contest/516/problem/B Description Drazil created a f ...

  8. Ordering Tasks(拓扑排序+dfs)

    Ordering Tasks John has n tasks to do. Unfortunately, the tasks are not independent and the executio ...

  9. 【紫书】Ordering Tasks UVA - 10305 拓扑排序:dfs到底再输出。

    题意:给你一些任务1~n,给你m个数对(u,v)代表做完u才能做v 让你给出一个做完这些任务的合理顺序. 题解:拓扑排序版题 dfs到底再压入栈. #define _CRT_SECURE_NO_WAR ...

  10. Codeforces Round #397 by Kaspersky Lab and Barcelona Bootcamp (Div. 1 + Div. 2 combined) E. Tree Folding 拓扑排序

    E. Tree Folding 题目连接: http://codeforces.com/contest/765/problem/E Description Vanya wants to minimiz ...

随机推荐

  1. MySQL之sql_mode

    sql_mode是个很容易被忽视的变量,默认值是空值,在这种设置下是可以允许一些非法操作的,比如允许一些非法数据的插入.在生产环境必须将这个值设置为严格模式,所以开发.测试环境的数据库也必须要设置,这 ...

  2. 【Java基础】-- isAssignableFrom的用法详细解析

    最近在java的源代码中总是可以看到isAssignableFrom()这个方法,到底是干嘛的?怎么用? 1. isAssignableFrom()是干什么用的? 首先我们必须知道的是,java里面一 ...

  3. springboot 参数注解 注入参数

    什么时注解参数 说明 我们在 使用spring mvc 的时候会使用这样的注解 @ResponseBody 这样,spring mvc 会将 客户端传来的数据,自动构建成 相应类型的对象. 有些情况下 ...

  4. 基于云主机的ModelArts模型训练实践,让开发环境化繁为简

    本文分享自华为云社区<[开发者空间实践]云主机安装Docker并制作自定义镜像在ModelArts平台做模型训练>,作者: 开发者空间小蜜蜂. 1.1 案例介绍 在AI业务开发以及运行的过 ...

  5. PM-企业数字化转型,数字化建设的重点

    在数字化转型深入推进的大背景下,加强数据管理,释放数据要素价值,实现企业数据价值的内部循环,形成企业数据资产,是各个企业顺应时代发展趋势,积极探索业绩新亮点的必由之路. 数字化转型四个阶段: 一. 业 ...

  6. Flutter (2.5) - A splash screen was provided to Flutter, but this is deprecated

    Flutter (2.5) - A splash screen was provided to Flutter, but this is deprecated 升级到Flutter 2.5 版本后,启 ...

  7. mysql命令行创建数据库并设置字符集

    CREATE DATABASE test1 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

  8. 开源for Huawei,Beam适配GaussDB实践案例分享

    沃土云创开源开发者专项计划是华为给开源开发者提供专属激励资源,鼓励开发者积极参与开源 for Huawei适配,践行"让优秀开发者支持更优秀开发者"的理念. 之前我们介绍了fake ...

  9. Qt/C++音视频开发59-使用mdk-sdk组件/原qtav作者力作/性能凶残/超级跨平台

    一.前言 最近一个月一直在研究mdk-sdk音视频组件,这个组件是原qtav作者的最新力作,提供了各种各样的示例demo,不仅限于支持C++,其他各种比如java/flutter/web/androi ...

  10. Many-shot Jailbreaking💘足够长的上下文长度有利于各种越狱?

    这篇文章虽然相较于上一篇图的对应有点迷,但是我感到了作者在强化学习与微调还有数学方面的深厚功底,我甚至感觉他的附录可以再发一篇文章了 这阶段的学习打开了我对越狱的思路~ 禁止盗用,侵权必究!!!欢迎大 ...