P7077 [CSP-S2020] 函数调用
调用是一个拓扑图
乘法: 每个数贡献这个倍数次
通过拓扑图的DP求出每个函数被调用后对a的影响(先乘后加,乘法要更新加的个数)
注意调用顺序;
建立一个虚拟原点0号连接所有调用的点
点击查看代码
#include <vector>
#include <stdio.h>
#include <string.h>
const int N = 1e5 + 5, M = 2e6 + 5;
const int mod = 998244353;
typedef long long LL;
int n, m, l;
int a[N];
std::vector<int> g1[N], g2[N]; // 两个图:一个正图,一个反图
int type[N], pos[N], val[N];
int cnt[N]; // 每个加法标记的影响
int q[N], deg[N];
int mul[N]; // 乘法标记:执行完一次后的扩倍情况
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i ++) scanf("%d", a + i);
scanf("%d", &m);
for(int i = 1, k, v; i <= m; i ++) {
scanf("%d", type + i);
if(type[i] == 1) scanf("%d%d", pos + i, val + i), mul[i] = 1;
else if(type[i] == 2) scanf("%d", mul + i);
else if(type[i] == 3) {
scanf("%d", &k), mul[i] = 1;
while(k --)
scanf("%d", &v), g1[v].push_back(i), g2[i].push_back(v);
}
}
cnt[0] = mul[0] = 1, scanf("%d", &l);
for(int i = 1, x; i <= l; i ++)
scanf("%d", &x), g1[x].push_back(0), g2[0].push_back(x);
int hh = 0, tt = 0;
for(int i = 0; i <= m; i ++) deg[i] = g2[i].size(), !deg[i] && (q[tt ++] = i);
while(hh != tt) {
int u = q[hh ++];
for(int v : g1[u]) {
mul[v] = LL(mul[v]) * mul[u] % mod;
if(-- deg[v] == 0) q[tt ++] = v;
}
}
hh = tt = 0;
for(int i = 0; i <= m; i ++) deg[i] = g1[i].size(), !deg[i] && (q[tt ++] = i);
while(hh != tt) {
int u = q[hh ++], now = 1; // now为当前乘积
for(int i = g2[u].size() - 1; i >= 0; i --) { // 注意顺序:只影响之前的函数
int v = g2[u][i];
cnt[v] = (cnt[v] + LL(cnt[u]) * now) % mod;
now = LL(now) * mul[v] % mod;
if(-- deg[v] == 0) q[tt ++] = v;
}
}
for(int i = 1; i <= n; i ++) a[i] = LL(a[i]) * mul[0] % mod;
for(int i = 1; i <= m; i ++)
if(type[i] == 1) a[pos[i]] = (a[pos[i]] + LL(cnt[i]) * val[i]) % mod; // 统计加法的贡献
for(int i = 1; i <= n; i ++) printf("%d ", a[i]);
return 0;
}