【题解】P7077 [CSP-S2020] 函数调用

题目链接(洛谷)

题目大意

给出三类函数,分别可以实现下面三种功能:

  1. 单点加;

  2. 全局乘;

  3. 依次执行若干次函数调用,保证不会出现递归。

给出个数据的出初始值,并给出个函数调用,求最后的数据。

$1\le n \le 10^5$

分析

一个很重要的结论:

某个函数被调用后被乘$k$,等价于这个函数被执行$k$次。

于是操作2和3都可以转化为操作1执行若干次。

函数调用形成一个,将它的调用次数设为。进行拓扑排序,递推出每一个函数的调用次数,最后直接处理每个操作;先用一个拓扑排序把每个函数的乘的倍数算出来,然后递推出执行次数即可。

实现

$(2.79s,~14.94mb)$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 100005;
const int mod = 998244353;
int n, m, c, a[N], deg[N], func[N],com[N], pos[N], val[N];
ll mu = 1, mul[N], dp[N], add[N];
vector<int> e[N];
queue<int> q;
bool vis[N];
void dfs(int id) {
vis[id] = 1, mul[id] = (com[id] == 2 ? val[id] : 1);
for (int it:e[id]) {
if (!vis[it]) dfs(it);
mul[id] = mul[id] * mul[it] % mod;
}
}

int main() {
cin>>n;
for (int i = 1; i <= n; i++) cin>>a[i];
cin>>m;
for (int i = 1; i <= m; i++) {
cin>>com[i];
if (com[i] == 1) cin>>pos[i]>>val[i];
else if (com[i] == 2)cin>> val[i];
else {
cin>>c;
while (c--) {
int to;
cin>>to;
e[i].push_back(to), deg[to]++;
}
}
}
cin>>c;
for (int i = 1; i <= m; i++){
if (!vis[i] && !deg[i])dfs(i);
}
for (int i = 1; i <= c; i++){
cin>>func[i];
}
for (int i = c, f = func[i]; i; i--, f = func[i]) {
if (com[f] == 1) dp[f] = (dp[f] + mu);
else if (com[f] == 2)mu = mu * val[f] % mod;
else dp[f] = (dp[f] + mu), mu = mu * mul[f] % mod;
}
for (int i = 1; i <= m; i++){
if (!deg[i]){
q.push(i);
}
}
while (!q.empty()) {
int t = q.front();
q.pop();
if (com[t] == 1){
add[pos[t]] = (add[pos[t]] + dp[t] * val[t]) % mod;
}
ll z = dp[t];
reverse(e[t].begin(), e[t].end());
for (int p:e[t]) {
deg[p]--;
if (!deg[p])q.push(p);
dp[p] = (dp[p] + z) % mod, z = z * mul[p] % mod;
}
}
for (int i = 1; i <= n; i++) cout << (a[i] * mu + add[i]) % mod << " ";
return 0;
}