【题解】[CSP-S 2020] 函数调用【拓扑图DP】

题目链接

题目链接

(题意略)

题解

显然这是一个拓扑图。每个加法的贡献是所有后缀积之和。于是先逆序(前向星自带逆序)DP 出每个点对应的积,在 DP 中途枚举到 \(u\to v\) 的边时,把当前 \(u\) 的状态赋给 \(u\to v\),再 DP \(v\),最后更新 \(u\) 的状态。然后每条到加法操作的路径代表着调用一次加法操作,其后缀和即为路径边权积。然后再 DP 一遍就能得到每个加法操作的贡献。

代码:

#include<bits/stdc++.h>
using namespace std;
int getint(){
	int ans=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-')f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		ans=ans*10+c-'0';
		c=getchar();
	}
	return ans*f;
}
const int N=4e5+10,M=4e6+10,mod=998244353;
struct bian{
	int l,e,n;
};
bian b[M];
int s[N],tot=1;
void add(int x,int y){
	++tot;
	b[tot].l=1;
	b[tot].e=y;
	b[tot].n=s[x];
	s[x]=tot;
}
int n,m,q;
int op[N],pos[N],val[N];
int f[N];
int dfn[N],dfnn=0;
int ss1(int x){
	if(~f[x])return f[x];
	if(op[x]==1){
		f[x]=1;
	}else if(op[x]==2){
		f[x]=val[x];
	}else{
		f[x]=1;
		for(int i=s[x];i;i=b[i].n){
			b[i].l=f[x];
			f[x]=f[x]*1ll*ss1(b[i].e)%mod;
		}
	}
	dfn[++dfnn]=x;
	return f[x];
}
bool vis[N];
int g[N];
int ans[N];

int main(){
	n=getint();
	op[0]=3;
	for(int i=1;i<=n;i++){
		add(0,i);
		op[i]=1;
		pos[i]=i;
		val[i]=getint();
	}
	m=getint();
	for(int i=n+1;i<=n+m;i++){
		op[i]=getint();
		if(op[i]==1)pos[i]=getint(),val[i]=getint();
		else if(op[i]==2)val[i]=getint();
		else{
			int c=getint();
			while(c--)add(i,getint()+n);
		}
	}
	int q=getint();
	for(int i=0;i<q;i++)add(0,getint()+n);
	memset(f,-1,sizeof(f));
	ss1(0);
	g[0]=1;
	for(int I=dfnn;I;--I){
		int i=dfn[I];
		for(int j=s[i];j;j=b[j].n){
			int v=b[j].e;
			g[v]=(g[v]+g[i]*1ll*b[j].l)%mod;
		}
		if(op[i]==1){
			ans[pos[i]]=(ans[pos[i]]+g[i]*1ll*val[i])%mod;
		}
	}
	for(int i=1;i<=n;i++)printf("%d ",ans[i]);
	
//	cerr<<"\n\nFinished after "<<clock()/1000.0<<" second(s)."<<endl;
	return 0;
}
posted @ 2020-11-09 11:21  破壁人五号  阅读(385)  评论(0编辑  收藏  举报