P7077 [CSP-S2020] 函数调用

题意

分析

直接线段树合并似乎复杂度不太对,会被卡。

于是考虑线性做法,发现原题的每一个函数的调用关系构成一个DAG。

由部分分启示我们先想想只有1操作的情况:直接每次暴力单点加,最后查询一次就行了。

再看只有前两个的情况:每次还是暴力单点加,但是要统计一下这个操作后面一共乘了多少倍,那么我们可以看做这个加法执行了这么多倍次。

现在加上第三个操作,函数的存在让我们不能直接递归下去再用二情况的方法了,但是我们发现其实这样也无非是很多的单点加,可以只统计造成的次数的影响。

考虑这样处理:

现在我们只对于每一个操作来统计在这之后乘了多少倍。

这个过程我们可以通过一次\(dfs\)来实现,相当于每一个乘法的权值是\(val\),然后其他的初始值都是\(1\),接下来按照 \(\prod_{v\in son[u]}{val[v]}\) 计算每一个节点的值即可。

然后统计倍数就是后缀的乘积了。

然后把这个次数递归到子节点去。

同时,我们在上一步的过程中,需要从后往前遍历子节点,并记录沿途的乘数之积。

因为我们在调用同一个函数前面的操作的时候,后面的乘法也会对前面的加法造成影响。

所以把次数递归到子节点的时候,还要乘上当前的乘数。

最后在叶子节点的加法,我们直接算一下贡献即可,同时记得使用最后的倍数乘以原本的每一个数。

代码

#include<bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
	x=0;char ch=getchar();bool f=false;
	while(!isdigit(ch)){f|=ch=='-';ch=getchar();}
	while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
	x=f?-x:x;
	return ;
}
template <typename T>
inline void write(T x){
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);
	putchar(x%10^48);
	return ;
}
#define ll long long
#define pb push_back
const int N=1e6+5,M=1e6+6;
const ll MOD=998244353;
int n,m,q,tp[N],id[N],in[N];
ll val[N],mul[N],add[N],pos[N],Mul[N],num[N];
vector<int>vec[N];
bool vis[N];
inline ll inc(ll x,ll y){return x+y>=MOD?x+y-MOD:x+y;}
void dfs(int x){
	if(vis[x]) return ;
	vis[x]=true;
	const int len=vec[x].size();
	for(int y:vec[x]) dfs(y),mul[x]=mul[x]*mul[y]%MOD;
	return ;
}
void topusort(){
	queue<int>q;
	for(int i=1;i<=m;i++) if(!in[i]) q.push(i);
	while(!q.empty()){
		int x=q.front();q.pop();
		const int len=vec[x].size();
		ll MUL=1;
		for(int i=len-1;i>=0;i--){
			int y=vec[x][i];
			num[y]=inc(num[y],MUL*num[x]%MOD);
			in[y]--;
			if(!in[y]) q.push(y);
			MUL=MUL*mul[y]%MOD;
		}
	}
	return ;
}
signed main(){
	read(n);
	for(int i=1;i<=n;i++) read(val[i]);
	read(m);
	for(int i=1;i<=m;i++){
		read(tp[i]);mul[i]=1;
		if(tp[i]==1) read(pos[i]),read(add[i]);
		else if(tp[i]==2) read(mul[i]);
		else{
			int len;read(len);
			for(int j=1,x;j<=len;j++) read(x),vec[i].pb(x),in[x]++;
		}
	}
	for(int i=1;i<=m;i++) if(!vis[i]) dfs(i);
	read(q);
	for(int i=1;i<=q;i++) read(id[i]);
	Mul[q+1]=1;
	for(int i=q;i>=1;i--){
		Mul[i]=Mul[i+1]*mul[id[i]]%MOD;
		num[id[i]]=inc(num[id[i]],Mul[i+1]);
	}
	topusort();
	for(int i=1;i<=n;i++) val[i]=val[i]*Mul[1]%MOD;
	for(int i=1;i<=m;i++) if(tp[i]==1) val[pos[i]]=inc(val[pos[i]],add[i]*num[i]%MOD);
	for(int i=1;i<=n;i++) write(val[i]),putchar(' ');
	
	return 0;
}
posted @ 2021-07-21 21:23  __Anchor  阅读(317)  评论(0)    收藏  举报