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

转化

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

当我们进行一个操作一后,假设当前节点的增量为 \(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. elasticsearch-head插件基本使用

    1. 查看搜索setting信息 mp_index/_settings 2. 设置分片数量 3, 修改数据刷新间隔 { "refresh_interval": "30s& ...

  2. 【实战问题】-- 布隆过滤器的三种实践:手写,Redission以及Guava(2)

    前面我们已经讲过布隆过滤器的原理[实战问题]-- 缓存穿透之布隆过滤器(1),都理解是这么运行的,那么一般我们使用布隆过滤器,是怎么去使用呢?如果自己去实现,又是怎么实现呢? 目录 布隆过滤器 手写布 ...

  3. DTL事务控制语言--sql事务

    DTL事务控制语言体格sql语句就是一个事务事务可以保证 一组sql语句要么都成功,要么都失败默认自动提交一可以关闭 set autocommit=0关闭自动提交最后 插入或者修改时 只有commit ...

  4. 攻防世界:web习题之xff_referer

    攻防世界:web习题之xff_referer 题目内容 https://adworld.xctf.org.cn/challenges/list 这道题的网页会首先会显示: 当成功修改ip地址之后会提示 ...

  5. 《前端运维》一、Linux基础--11服务

    首先服务的概念,对于linux服务器来说,其中大多数的软件都算是服务,比如nginx.mysql.Jenkins等等.下面我们就来学习一下linux中有关服务的一些内容. 一.服务简介和分类 1.运行 ...

  6. shell 读取文件内容到数组

    在 shell 脚本中,可以使用下面的语法来读取文件内容并将其存储到数组中:   bash 复制代码 array=() while read line; do array+=("$line& ...

  7. 从 ftp 上下载文件、文件夹

    下载子文件夹: wget -r -nH --cut-dir=1 ftp://ip/folder_name/ 下载压缩文件: wget ftp://ip/folder_name/folder_name/ ...

  8. re模块:核心函数和方法

    1.compile(pattren,flages=0)   使用任何可选的标记来编译正则表达式的模式然后返回一个正则表达式对象 2.match(pattern,string,flags=0)    尝 ...

  9. 绞尽脑汁终于搞定/天地图标注点marker旋转/任意角度旋转/无需引入其他框架

    一.前言说明 在其他地图组件中,标注点marker都是可以设置旋转角度的,这个功能其实非常实用,比如飞机移动轨迹,就是需要旋转飞机头飞行,轮船轨迹移动也是,百度地图和腾讯地图是通过调用setRotat ...

  10. Qt数据库应用2-数据导出到xls

    一.前言 导入导出数据到csv由于语法简单,适用场景有限,于是乎还是必须再造一个轮子导出数据到xls,在经历过数十年的项目实战经验中不断调整和优化.尤其记得当初第一个版本v0.01大概在2011年左右 ...