函數調用[CSP2020]


題目描述

https://www.luogu.com.cn/problem/P7077

題解

第一眼看像是數據結構題?

后來發現這題壓根不用數據結構

考慮對於給出的操作建一個圖:對於所有操作3,按順序向它調用的函數連邊,這樣會得到一個DAG

對於乘法操作,在最后給所有數組元素乘上就好了,關鍵在於每個加法操作最后乘了一個多大的系數

假設整個數組只有一個元素,對它依次執行:+1, *3, +2, *2

那么+1操作實際上就有一個2*3=6的系數,+2有2的系數,所以假設原來這個元素是 \(x\),那它最后會是 \(6x+1\times 6+ 2\times 2\)

發現一個加法操作帶的系數就等於它后面的所有乘法操作之積,所以可以倒着進行操作,一邊記錄已進行的所有乘法操作的積是多少,這樣就能計算出每次加法操作帶的系數是多少

至此,只含1,2操作的情況就處理完了,接下來考慮3操作

對於圖上的每個點(代表着一種操作),維護一個mul屬性,表示執行一次這個操作會給累計的積乘上多少

對於1類操作,它的mul=1;對於2類操作,它的mul就等於它要乘上的值;而對於3類操作,它的mul等於它直接連向的所有點的mul之積

如圖,點2的mul為2,點3的mul為3,所以點1的mul為6,那么執行一次操作1就會讓前面執行過的所有的加法操作再乘上6的系數

按照拓撲序倒序掃一遍或者直接dfs即可處理出mul

然后倒着進行 \(q\) 次操作,就可以求得每次操作(類型1或3)帶着多少系數,記作sum,然后再把類型3的節點的sum下傳到它所包含的類型1節點即可

但是有些類型3的操作既包含加法又包含乘法怎么辦?

108_2.png

考慮這樣一張圖 假設編號為1的操作的sum是 \(x\),那么+2這個操作的sum應該額外增加 \(3x\),同理+1的sum應增加 \(12x\)

所以下傳sum時,假設一個點 \(x\) 的sum是 \(S\),它的兒子是 \(y_1,y_2,\cdots y_k\)
那么 \(y_i\) 的sum就應該增加 \(S\) 乘上 \(y_{i+1}\sim y_k\) 的mul之積

最后,讓數組的每個元素乘上所有 \(q\) 次操作的mul,再遍歷所有加法操作計算應該加多少即可

代碼

#include <bits/stdc++.h>
#define N 1000005
using namespace std;
typedef long long ll;

template <typename T>
inline void read(T &num) {
	T x = 0; char ch = getchar();
	for (; ch > '9' || ch < '0'; ch = getchar());
	for (; ch <= '9' && ch >= '0'; ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ '0');
	num = x; 
}

const ll mod = 998244353;
int n, m, Q, F[N];
int head[N], pre[N<<1], to[N<<1], sz, inde[N];
ll a[N];

inline void addedge(int u, int v) {
	pre[++sz] = head[u]; head[u] = sz; to[sz] = v; inde[v]++;
}

struct oper {
	int tp, p;
	ll v, mul, sum; 
} b[N];

queue<int> q;
int ord[N], bnbn;
void toposort() { //拓撲排序
	for (int i = 1; i <= m; i++) if (!inde[i]) q.push(i);
	while (!q.empty()) {
		int x = q.front(); q.pop();
		ord[++bnbn] = x;
		for (int i = head[x]; i; i = pre[i]) {
			int y = to[i];
			inde[y]--;
			if (!inde[y]) q.push(y);
		}
	}
}

void getmul() { //計算節點的mul
	for (int i = m; i; i--) {
		int x = ord[i];
		for (int j = head[x]; j; j = pre[j]) {
			int y = to[j];
			b[x].mul = b[x].mul * b[y].mul % mod;
		}
	} 
}

void getsum() { //下傳節點的sum
	for (int i = 1; i <= m; i++) {
		int x = ord[i]; ll now = 1;
		for (int j = head[x]; j; j = pre[j]) {
			int y = to[j];
			b[y].sum = (b[y].sum + b[x].sum * now % mod) % mod;
			now = now * b[y].mul % mod;
		}
	}
}

int main() {
	read(n); 
	for (int i = 1; i <= n; i++) read(a[i]); 
	read(m);
	for (int i = 1; i <= m; i++) {
		read(b[i].tp);
		if (b[i].tp == 1) {
			read(b[i].p); read(b[i].v);
			b[i].mul = 1;
		} else if (b[i].tp == 2) {
			read(b[i].v); b[i].mul = b[i].v;
		} else {
			read(b[i].p); b[i].mul = 1;
			for (int j = 1, x; j <= b[i].p; j++) {
				read(x);
				addedge(i, x);
			}
		}
	}
	toposort(); 
	getmul();
	read(Q); ll now = 1;
	for (int i = 1; i <= Q; i++) read(F[i]);
	for (int i = Q; i; i--) {
		int x = F[i]; b[x].sum = (b[x].sum + now) % mod;
		now = now * b[x].mul % mod;
	}
	getsum();
	for (int i = 1; i <= n; i++) a[i] = a[i] * now % mod;
	for (int i = 1; i <= m; i++) {
		if (b[i].tp == 1) {
			a[b[i].p] = (a[b[i].p] + b[i].v * b[i].sum % mod) % mod;
		}
	}
	for (int i = 1; i <= n; i++) printf("%lld ", a[i]);
	return 0; 
} 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM