CSP-S 2020

( ext{Solution})

其实并不是一道难题,只是不知道如何一步步算。

首先想算出每个 (1) 操作的系数,这样能省去很多重复步骤。

观察到 (1) 操作可以被分成单个函数(指直接出现在函数操作序列中)和被引用函数(被直接出现在函数操作序列中的第 (3) 类函数引用)。

对于每个(一种函数可能在函数操作序列中被直接调用多次)单个函数我们需要算出在调用它之后调用的 (2) 操作之积,显然这就是对于这个单个函数的系数。我们将这种函数的每个单个函数的系数相加,显然就是这种函数的单个函数的总系数。

那被引用函数呢?我们只要向上面那样,算出第 (3) 类函数的系数,再下传给它就好了(注意这是一个 ( ext{DAG}),所以用拓扑排序来下传)。

详见注释。

( ext{Code})

#include <cstdio>
#define rep(i,_l,_r) for(signed i=(_l),_end=(_r);i<=_end;++i)
#define print(x,y) write(x),putchar(y)

int read() {
	int x=0,f=1; char s;
	while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
	while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
	return x*f;
}

void write(int x) {
	if(x<0) return (void)(putchar('-'),write(-x));
	if(x>9) write(x/10);
	putchar(x%10^48);
}

#include <queue>
#include <vector>
using namespace std;

const int maxn=1e5+5,mod=998244353;

int n,m,Q,a[maxn],op[maxn],pos[maxn],val[maxn],id[maxn],deg[maxn],mul[maxn],Add[maxn],f[maxn];
vector <int> g[maxn];
queue <int> q;
bool vis[maxn];

void dfs(int u) {
	if(vis[u]) return;
	vis[u]=1;
	mul[u]=(op[u]==2?val[u]:1); // mul: 直接引用第 3 类函数会导致翻多少倍
	for(int i=0;i<g[u].size();++i) {
		int v=g[u][i];
		dfs(v);
		mul[u]=1ll*mul[u]*mul[v]%mod;
	}
}

void topol() {
	int Mul;
	rep(i,1,m) if(!deg[i]) q.push(i);
	while(!q.empty()) {
		int u=q.front(); q.pop();
		if(op[u]==1) Add[pos[u]]=(Add[pos[u]]+1ll*f[u]*val[u]%mod)%mod; // 注意不能在后面插入队列时更新,不然本来就无子的就无法更新
		Mul=f[u];
		for(int i=g[u].size()-1;i>=0;--i) {
			int v=g[u][i];
			--deg[v];
			if(!deg[v]) q.push(v);
			f[v]=(f[v]+Mul)%mod; Mul=1ll*Mul*mul[v]%mod; // 这里是为了算间接引用时 v 的贡献:父亲们的系数 * 在自己后面的兄弟的系数
		}
	} 
}

int main() {
	int x,y;
	n=read();
	rep(i,1,n) a[i]=read();
	m=read();
	rep(i,1,m) {
		op[i]=read();
		if(op[i]==1) pos[i]=read(),val[i]=read();
		else if(op[i]==2) val[i]=read();
		else {
			x=read();
			rep(j,1,x) y=read(),++deg[y],g[i].push_back(y);
		}
	}
	Q=read();
	rep(i,1,Q) id[i]=read();
	rep(i,1,m) if(!deg[i]) dfs(i);
	y=1;
	for(int i=Q;i>=1;--i) {
		x=id[i];
		if(op[x]==1) f[x]=(f[x]+y)%mod;
		else if(op[x]==2) y=1ll*y*mul[x]%mod;
		else f[x]=(f[x]+y)%mod,y=1ll*y*mul[x]%mod; // f 就是算函数在函数调用序列里翻了多少倍
	}
	topol();
	rep(i,1,n) print((1ll*a[i]*y%mod+Add[i])%mod,' '); puts("");
	return 0;
}
原文地址:https://www.cnblogs.com/AWhiteWall/p/14022221.html