P7077 [CSP-S2020] 函数调用

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;
}
posted @ 2022-09-28 15:44  azzc  阅读(47)  评论(0编辑  收藏  举报