题解:CSP-S2020] 函数调用
-
一句话题意:给定一个有初始值的序列,支持如下三种操作:
- 1、单点加
- 2、全局乘
- 3、递归某些操作1、2、3
-
求最终的序列。
-
标签:topsort,动态规划,转化贡献统计(集中贡献),主客翻转
-
关于topsort:部分分里的树结构基本上直接暗示了正解要使用topsort,而且本来函数关系就会组成一张DAG。
-
关于贡献转化:
- 加、乘运算的独立比较容易想到:即某个元素的终值=初值*全局乘因子+扩倍后每个加法因子
- 简单来说,
- 加、乘运算的独立比较容易想到:即某个元素的终值=初值*全局乘因子+扩倍后每个加法因子
-
感觉难点在于想到贡献的统计要全部转化到操作1上
-
如果针对每个操作统计贡献,那意味着每个操作上要挂原序列中的 \(n\) 个数,显然不管是空间还是时间,这都是无法承受的。而我们可以通过动态规划的方式将所有贡献转到操作1上。(搞了半天精髓还是在动规上!!!)
- 全局乘因子可以一遍倒序topsort求得
- 扩倍后每个加法因子可以一遍正序top求得,思想见这篇博客
- 简单来说就是同父亲的儿子的乘法因子受到更靠右的儿子的影响,链式前向星的遍历顺序天然地满足我们从右到左遍历儿子的需求。
- 最后合并贡献
a[b[i].p]=((a[b[i].p]+1ll*b[i].v*b[i].sum)%mod+mod)%mod;
(存疑:为何还要乘
b[i].v
???sum中没有包含吗?)
#include<bits/stdc++.h>
#define F(i,l,r) for(int i(l);i<=r;++i)
#define G(i,r,l) for(int i(r);i>=l;--i)
using namespace std;
using ll = long long;
char buf[100],*p1=buf,*p2=buf;
inline int gc(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100,stdin),p1==p2)?EOF:*p1++;}
inline int rd(){
int x=0; char ch;
while(!isdigit(ch=gc()));
do x=(x<<3)+(x<<1)+(ch^48); while(isdigit(ch=gc()));
return x;
}
const int N=1e6+5;
const int mod=998244353;
struct node{
int op,p,v,mul,sum;
}b[N];
struct edge{
int v,ne;
}e[N<<1];
int n,m,q,idx=0,cnt=0;
int in[N],a[N],first[N],ord[N],f[N];
void add(int x,int y){e[++idx]=(edge){y,first[x]};first[x]=idx;}
void topsort(){
queue<int> q;
F(i,1,m) if(!in[i]) q.emplace(i);
while(q.size()){
int u=q.front(); q.pop();
ord[++cnt]=u;
for(int i=first[u];i;i=e[i].ne){
int v=e[i].v;
if(!(--in[v])) q.emplace(v);
}
}
}
void getmul(){
G(o,m,1){
int u=ord[o];
for(int i=first[u];i;i=e[i].ne){
int v=e[i].v;
b[u].mul=(1ll*b[u].mul*b[v].mul)%mod;
// nw=(1ll*nw*b[u].mul)%mod;
}
}
}
void getsum(){
F(o,1,m){
int u=ord[o],nw=1;
for(int i=first[u];i;i=e[i].ne){
int v=e[i].v;
b[v].sum=(1ll*b[v].sum+1ll*nw*b[u].sum)%mod;
nw=(1ll*nw*b[v].mul)%mod;
}
}
}
signed main(){
n=rd();
F(i,1,n) a[i]=rd();
m=rd();
F(i,1,m){
b[i].op=rd();
if(b[i].op==1){
b[i].p=rd(),b[i].v=rd();
b[i].mul=1;
}
else if(b[i].op==2){
b[i].v=rd();
b[i].mul=b[i].v;
}
else{
b[i].p=rd();
b[i].mul=1;
F(j,1,b[i].p){
int o=rd();
add(i,o);
in[o]++;
}
}
}
topsort();
getmul();
q=rd(); int nw=1;
F(i,1,q) f[i]=rd();
G(i,q,1){
int x=f[i];
b[x].sum=(b[x].sum+1ll*nw)%mod;
nw=(1ll*nw*b[x].mul)%mod;
}
getsum();
F(i,1,n) a[i]=(1ll*a[i]*nw)%mod;
F(i,1,m){
if(b[i].op==1){
a[b[i].p]=((a[b[i].p]+1ll*b[i].v*b[i].sum)%mod+mod)%mod;
}
}
F(i,1,n) printf("%d ",a[i]);
return 0;
}
- 总结:答案贡献分散,难以统计时,考虑转化统计主体,以集中贡献,优化算法。