zoukankan      html  css  js  c++  java
  • P7077 函数调用(CSP-S2020 T3)

    题目传送门

    思路

    考场上只花(10min)打了个(10pts)递归暴力跑路了。其实一眼看出是线段树2的板子,但是由于只剩半小时而且还没打(T4),所以果断放弃了。

    其实这个题跟线段树完全没有关系,因为线段树最强的一点就是可以一边修改一边查询,但是本题只需要输出最后结果就行了,所以显然没有发挥出线段树的长处。然后仔细想了一个小时,思考出了个寂寞,果断看题解。看了五个小时终于看懂了,非常感动。

    心态崩了,所以不想写思路,那些思路虽然已经深入我的脑海,但是我确实是不懂,只能把看懂的写在注释里。

    最重要的思路其实就是倒着处理吧,因为只有先加后乘,乘上的数才能给加贡献,所以打标记的时候一定要倒着来。

    代码

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<queue>
    using namespace std;
    typedef long long ll;
    const int N=1000005,mod=998244353;
    int n,m,Q,k;
    int in[N],f[N],op[N],p[N],qry[N];//f数组代表若第i个函数是类型1函数,则对第i个函数的加标记的贡献的倍数
    ll a[N],mu=1,mul[N],w[N],add[N];//mul数组记录对全局乘标记的贡献,mu就是全局乘标记,add数组记录最后a数组乘完全局乘标记之后需要加上的数
    bool vis[N];
    vector<int> h[N];
    queue<int> q;
    void dfs(int num){//有点记搜的思想,先预处理所有的函数
    	vis[num]=1;
    	if(op[num]==2){
    		mul[num]=w[num];
    	}
    	else{
    		mul[num]=1;
    	}
    	for(int i=0;i<h[num].size();i++){
    		int to=h[num][i];
    		if(!vis[to]) dfs(to);
    		mul[num]=mul[num]*mul[to]%mod;//更新一遍
    	}
    }
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++){
    		scanf("%lld",&a[i]);
    	}
    	scanf("%d",&m);
    	for(int i=1;i<=m;i++){
    		scanf("%d",&op[i]);
    		if(op[i]==1){
    			scanf("%d%d",&p[i],&w[i]);
    		}
    		if(op[i]==2){
    			scanf("%d",&w[i]);
    		}
    		if(op[i]==3){
    			scanf("%d",&k);
    			for(int j=1;j<=k;j++){
    				int numm;
    				scanf("%d",&numm);
    				h[i].push_back(numm);
    				in[numm]++;//为拓扑排序做准备
    			}
    		}
    	}
    	for(int i=1;i<=m;i++){
    		if(!in[i]&&!vis[i]) dfs(i);//如果在其他函数内就不用单独再预处理一遍了,直接从入度为0的点开始dfs即可
    	}
    	scanf("%d",&Q);
    	for(int i=1;i<=Q;i++){
    		scanf("%d",&qry[i]);
    	}
    	for(int i=Q;i>=1;i--){
    		if(op[qry[i]]==1){
    			f[qry[i]]=(f[qry[i]]+mu)%mod;
    		}
    		if(op[qry[i]]==2){
    			mu=(w[qry[i]]*mu)%mod;
    		}
    		if(op[qry[i]]==3){
    			f[qry[i]]=(f[qry[i]]+mu)%mod;
    			mu=(mul[qry[i]]*mu)%mod;//三种函数分别对全局的影响
    		}
    	}
    	for(int i=1;i<=Q;i++){
    		if(!in[i]) q.push(i);
    	}
    	while(!q.empty()){
    		int x=q.front();
    		q.pop();
    		if(op[x]==1) add[p[x]]=(add[p[x]]+f[x]*w[x]%mod)%mod;//这里入队的函数一定不可能再被3类型的函数更新了,因为它的入度已经是0了,所以可以直接更新最后的结果了
    		for(int i=h[x].size()-1;i>=0;i--){//这里也要倒着处理
    			int to=h[x][i];
    			in[to]--;if(!in[to]) q.push(to);
    			f[to]=(f[to]+f[x])%mod,f[x]=(f[x]*mul[to])%mod;
    		}
    	}
    	for(int i=1;i<=n;i++){
    		printf("%lld ",(a[i]*mu+add[i])%mod);
    	}
    	return 0;
    }
    
  • 相关阅读:
    MySQL —— 程序连接时的驱动名称和URL
    这该称作什么效应?
    Java 基础 面向对象之关键字内部类代码块修饰符
    Java 基础 面向对象之构造方法和关键字
    Java 基础 接口和多态
    Java 基础 面向对象和抽象类
    Java 基础 引用数据类型 ArrayList集合
    Java 基础 方法
    Java 基础 引用数据类型 和 流程控制
    Mongodb 基础 复制集原理和搭建
  • 原文地址:https://www.cnblogs.com/57xmz/p/14070970.html
Copyright © 2011-2022 走看看