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

转化

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

当我们进行一个操作一后,假设当前节点的增量为 \(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. python之Marshmallow

    文档说明:https://marshmallow.readthedocs.io marshmallow是一个用来将复杂的orm对象与python原生数据类型之间相互转换的库,简而言之,就是实现obje ...

  2. django admin 后台管理 新手学习步骤记录 (2)

    学习使用django admin后台管理. 参考.Django基础之Admin后台数据管理_django admin_马航行的博客-CSDN博客

  3. C#调用Python脚本的方式(一),以PaddleOCR-GUI为例

    前言 每种语言都有每种语言的优势,Python由于其强大的生态,很多任务通过调用包就可以实现,那么学会从C#项目中调用Python脚本完成任务就很重要.C#调用Python代码有多种方式,如果Pyth ...

  4. Shiro安全框架【认证】+【授权】

    1.Shiro的核心架构 Subject:程序主体 Security Manager:安全管理器 Authentication:做认证 Authorizer:做授权 Session Manager:会 ...

  5. Table flags are 0 in the data dictionary but the flags in file ./ibdata1 are 0x4800!

    1.问题截图 cat /var/log/mysql/error.log 2019-01-28T09:49:57.076019Z 0 [ERROR] [FATAL] InnoDB: Table flag ...

  6. Qt编写视频监控系统74-悬浮工具栏(半透明/上下左右位置/自定义按钮)

    一.前言 在监控系统中一般在视频实时预览的时候,希望提供一个悬浮工具条,可以显示一些提示信息比如分辨率.码率.帧率,提供一堆快捷操作按钮,可以录像.抓拍.云台控制.关闭等操作,参考了国内很多监控厂商客 ...

  7. 关于Qt选择qml还是widget的深度思考

    在Qt界始终有两大阵营产生激烈的纷争,那就是选用qml还是widget好,大量初学者也会问这个问题,有以下几点总结. widget属于传统界面开发,和VB/VC/Delphi等拖曳控件开发类似,走CP ...

  8. Qt编写的项目作品34-雷达模拟仿真工具(雨田哥作品)

    一.功能特点 支持音频频谱显示. 支持任意随机添加模拟点. 支持自定义添加模拟点. 支持方位.航向角.距离.速度.目标体真实图自定制. 支持危险区域范围显示. 支持激光发射模拟. 支持雷达图放大缩小显 ...

  9. ASP.NET Core 中的速率限制中间件的使用

    简介 在ASP.NET Core中,速率限制中间件是用来控制客户端对Web API或MVC应用程序发出请求的速率,以防止服务器过载和提高安全性. 下面是 AddRateLimiter 的一些基本用法: ...

  10. 字符串编码(ASCII, GBK, ANSI, Unicode(‘u‘), UTF-8编码)(转载)

    [版权声明]本篇文章以征得博主同意,再行转载. 出自[hxxjxw] 原文链接:https://blog.csdn.net/hxxjxw/article/details/90140663 目录 字符串 ...