题解:CSP-S2020] 函数调用

题解:CSP-S2020] 函数调用

  • 一句话题意:给定一个有初始值的序列,支持如下三种操作:

    • 1、单点加
    • 2、全局乘
    • 3、递归某些操作1、2、3
  • 求最终的序列。

  • 标签:topsort,动态规划,转化贡献统计(集中贡献),主客翻转

  • 关于topsort:部分分里的树结构基本上直接暗示了正解要使用topsort,而且本来函数关系就会组成一张DAG。

  • 关于贡献转化

    • 加、乘运算的独立比较容易想到:即某个元素的终值=初值*全局乘因子+扩倍后每个加法因子
      • 简单来说,

\[((a+b)\times c+d)\times e=a \times c \times d+b \times c \times e +d \times e \]

  • 感觉难点在于想到贡献的统计要全部转化到操作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;
} 
  • 总结:答案贡献分散,难以统计时,考虑转化统计主体,以集中贡献,优化算法。
posted @ 2024-04-19 22:15  superl61  阅读(9)  评论(0编辑  收藏  举报