【拓扑排序】P7077 [CSP-S2020] 函数调用

这篇博客介绍了一种利用拓扑排序处理函数调用的方法,涉及单点加、全局乘等操作。通过建立函数调用的DAG图,并进行两次拓扑排序,分别计算全局乘值和加法的贡献,最终求解每个元素经过所有函数操作后的结果。代码中展示了如何实现这一过程。
摘要由CSDN通过智能技术生成

题意

n n n个元素,进行一些函数的操作。
功能可分为三类:

  1. 单点加;
  2. 全局乘;
  3. 依次执行若干次函数调用,保证不会出现递归(即不会直接或间接地调用本身)。

思路

考虑给这些函数的调用关系建图,得到一个DAG。

若无操作1,则用拓扑排序计算出每个函数调用后给全局乘上的值。

考虑算上操作1,那么对于一个加法后的乘法,会给加法的贡献也乘上一个权值,那么可以倒着进行一次拓扑排序,算出每个加法被乘上之后的贡献。

代码

#include <queue>
#include <vector>
#include <cstdio>
#define int long long

const int mod = 998244353;

std::vector<int> G1[100001], G2[100001];
int n, m;
int a[100001], mul[100001], cnt[100001], pos[100001], add[100001], deg[100001];

void topsort1() {
	std::queue<int> q;
	for (int i = 0; i <= m; i++) {
		deg[i] = G2[i].size();
		if (!deg[i])
			q.push(i);
	}
	while (q.size()) {
		int x = q.front();
		q.pop();
		for (int i = 0; i != G1[x].size(); i++) {
			int y = G1[x][i];
			(mul[y] *= mul[x]) %= mod;
			deg[y]--;
			if (!deg[y])
				q.push(y);
		}
	}
}

void topsort2() {
	std::queue<int> q;
	for (int i = 0; i <= m; i++) {
		deg[i] = G1[i].size();
		if (!deg[i])
			q.push(i);
	}
	while (q.size()) {
		int x = q.front();
		q.pop();
		int now = 1;
		for (int i = G2[x].size() - 1; i >= 0; i--) {
			int y = G2[x][i];
			(cnt[y] += now * cnt[x] % mod) %= mod;//具体地,通过计算每个函数操作次数来算贡献
			(now *= mul[y]) %= mod;
			deg[y]--;
			if (!deg[y])
				q.push(y);
		}
	}
}

signed main() {
	scanf("%lld", &n);
	for (int i = 1; i <= n; i++)
		scanf("%lld", &a[i]);
	scanf("%lld", &m);
	mul[0] = 1;
	for (int i = 1; i <= m; i++) {
		mul[i] = 1;
		int typ;
		scanf("%lld", &typ);
		if (typ == 1) {
			scanf("%lld %lld", &pos[i], &add[i]);
		} else if (typ == 2) {
			scanf("%lld", &mul[i]);
		} else {
			int len;
			scanf("%lld", &len);
			for (int j = 1, x; j <= len; j++) {
				scanf("%lld", &x);
				G1[x].push_back(i);
				G2[i].push_back(x);
			}
		}
	}
	int Q;
	scanf("%lld", &Q);
	for (int i = 1, x; i <= Q; i++) {
		scanf("%lld", &x);
		G1[x].push_back(0);
		G2[0].push_back(x);
	}
	cnt[0] = 1;
	topsort1();
	topsort2();
	for (int i = 1; i <= n; i++)
		(a[i] *= mul[0]) %= mod;
	for (int i = 1; i <= m; i++)
		(a[pos[i]] += cnt[i] * add[i] % mod) %= mod;
	for (int i = 1; i <= n; i++)
		printf("%lld ", a[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值