P7077 [CSP-S2020] 函式呼叫 - 拓撲序
阿新 • • 發佈:2021-10-05
題解
假如只有 \(1\) 操作,那麼操作之間可交換,可以建出 DAG,統計出每種 \(1\) 操作的執行次數即可。
可以發現,執行完一個操作 \(K\),再進行一次 \(2\ x\) 操作,相當於執行了 \(x\) 次 \(K\) 操作。
於是先倒序拓撲出每個操作執行完會被乘多少,然後正序拓撲即可。
程式碼
#include <cstdio> #include <cstring> #include <cctype> #include <algorithm> #include <vector> using namespace std; #define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti) #define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti) template<typename T> void Read(T &_x){ _x=0;int _f=1; char ch=getchar(); while(!isdigit(ch)) _f=(ch=='-'?-1:_f),ch=getchar(); while(isdigit(ch)) _x=_x*10+(ch^48),ch=getchar(); _x*=_f; } template<typename T,typename... Args> void Read(T &_x,Args& ...others){ Read(_x);Read(others...); } typedef long long ll; const int N=1e5+5,Mod=998244353; int n,m,tp[N],p[N],w[N],q,f[N]; ll a[N]; vector<int> g[N],topo,gg[N]; int que[N<<1],head=1,tail; int deg[N];ll mul[N],cnt[N]; int main(){ Read(n); For(i,1,n) Read(a[i]); Read(m); For(i,1,m){ Read(tp[i]); if(tp[i]==1) Read(p[i],w[i]); else if(tp[i]==2) Read(w[i]); else{ int k,x;Read(k); For(j,1,k){ Read(x); g[x].push_back(i),++deg[i]; gg[i].push_back(x); } reverse(gg[i].begin(),gg[i].end()); } mul[i]=1; } mul[0]=1; Read(q); For(i,1,q){ Read(f[i]); g[f[i]].push_back(0),++deg[0]; gg[0].push_back(f[i]); } reverse(gg[0].begin(),gg[0].end()); For(i,1,m) if(!deg[i]){ que[++tail]=i; if(tp[i]==2) mul[i]=w[i]; } while(head<=tail){ int u=que[head++]; topo.push_back(u); for(int v:g[u]){ mul[v]=mul[v]*mul[u]%Mod; if(--deg[v]==0) que[++tail]=v; } } reverse(topo.begin(),topo.end()); cnt[0]=1; for(int u:topo){ ll mu=1; for(int v:gg[u]){ cnt[v]=(cnt[v]+mu*cnt[u])%Mod; mu=mu*mul[v]%Mod; } } For(i,1,n) a[i]=a[i]*mul[0]%Mod; For(i,1,m){ if(tp[i]==1){ a[p[i]]=(a[p[i]]+cnt[i]*w[i])%Mod; } } For(i,1,n) printf("%lld ",a[i]); return 0; }