[CSP-S2020] 函数调用

题目传送门
比较绕一点。
貌似数据结构,实则不好维护。
假如说不考虑3操作,维护一个加标记和乘标记是好做的。注意到不会递归,于是把所有的3操作向他要执行的操作连边,形成了一个 DAG 。
对每个操作维护一个乘系数\(tag\),表示执行该操作后全体要乘上的值,显然对于1操作,\(tag\)\(1\),对于2操作,\(tag\)为该操作要乘的数,对于3操作,\(tag\)为他要执行的所有操作(也即 DAG 中他直接连向的所有点)之积。
拓扑排序以后,倒着扫一遍即可求出所有\(tag\)
\(tag\)是单次操作的乘系数,需要考虑再给定操作序列中的计算。记每个操作在全局中的乘系数为\(final\)
注意到对于每个加操作,只有在他之后的乘操作才会对他产生影响,于是倒着做,记录一个\(bac\)表示后缀的乘系数之积,我们令:
\(final_i=final_i+bac(i:Q\to1)\)
为什么要加?因为一个操作\(x\)如果出现不止一次,那么\(x\times bac+x\times bac'=x\times (bac+bac')\)
除此之外,由于所有3操作等价于再执行一遍某些1,2操作,因此3操作的\(final\)需要向他所连的点传递。传递时在每个3操作内部仍然是倒着做,过程与上面类似,因为一个3操作内部也会有加和乘。
事实上,上述过程把所有操作向加操作转换,最后只需:
\(a_i=a_i\times bac_1\)
\(a_{p_{x_i}}=a_{p_{x_i}}+v_{x_i}\times final_{x_i}\ (type_{x_i}=1)\)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pp pop_back
#define pb push_back
#define ins insert
#define lowbit(x) x & -x

const int N = 6e5 + 10;
const int mod = 998244353;
const ll INF = 1e18;

int read(){int x; scanf("%d", &x); return x; }
ll readll(){ll x; scanf("%lld", &x); return x; }

int n, m, Q;
int tops[N], ind[N], s[N];
ll a[N];
struct node{
	int type, pos, tt;
	ll tag, final, v;
}p[N];
vector<int>l[N];
queue<int>q;

signed main(){
	n = read();
	for(int i = 1; i <= n; i++) a[i] = readll();
	m = read();
	for(int i = 1; i <= m; i++){
		p[i].type = read();
		if(p[i].type == 1){
			p[i].pos = read(), p[i].v = read();
			p[i].tag = 1;
		} else if(p[i].type == 2){
			p[i].v = read();
			p[i].tag = p[i].v;
		} else {
			p[i].tt = read();
			for(int j = 1; j <= p[i].tt; j++){
				int id = read();
				l[i].pb(id);
				ind[id]++;
			}
			p[i].tag = 1;
		}
	}
	for(int i = 1; i <= m; i++) if(! ind[i]) q.push(i);
	int cnt = 0;
	while(q.size()){
		int x = q.front();
		q.pop();
		tops[++cnt] = x;
		for(auto y : l[x]){
			if(--ind[y] == 0) q.push(y);
		}
	}
	for(int i = m; i >= 1; i--){
		int x = tops[i];
		for(auto y : l[x]) (p[x].tag *= p[y].tag) %= mod;
	}
	Q = read();
	for(int i = 1; i <= Q; i++) s[i] = read();
	ll bac = 1;
	for(int i = Q; i; i--){
		int x = s[i];
		(p[x].final += bac) %= mod;
		(bac *= p[x].tag) %= mod;
	}
	for(int i = 1; i <= n; i++) (a[i] *= bac) %= mod;
	for(int i = 1; i <= m; i++){
		int x = tops[i];
		ll Bac = p[x].final;
		for(int i = l[x].size() - 1; i >= 0; i--){
			int y = l[x][i];
			(p[y].final += Bac) %= mod;//额外做一次,所以加上后面乘的 
			(Bac *= p[y].tag) %= mod;
		}
	}
	for(int i = 1; i <= m; i++){
		if(p[i].type == 1) (a[p[i].pos] += p[i].v * p[i].final % mod) %= mod;
	}
	for(int i = 1; i <= n; i++) cout << a[i] << ' ';
	return 0;
}
posted @ 2025-07-24 12:29  Lordreamland  阅读(15)  评论(0)    收藏  举报