zoukankan      html  css  js  c++  java
  • [CSP-S2020] 函数调用 题解

    明天就是CSP-S了 今天打算复习下板子 把这道T3题解顺便写一下吧

    题目大意是有三种函数

    1. 给数据中指定元素加上一个值
    2. 给数据所有元素都乘上一个值
    3. 调用1.2两种函数

    如果没有3操作 想必大家应该都会吧(坚信)

    一开始我也想到线段树 但是线段树只能拿30分

    逃出sb数据结构的圈子 认真想想此题

    题目说不存在调用自己的情况 也就是对于3操作来说它所调用的一定是之前的1,2函数 所以会发现整个函数调用关系事实上是一个有向无环图

    对于有向无环图处理方式就很多了,不妨考虑记忆化搜索或者dp

    我们发现 对于一个加法操作而言 如果它后面有若干乘法操作(假设操作了n次)

    (那么相当于它做了prod_{i = 1}^{n} val_{i} 次的加法)

    于是我们只需要记下每个函数调用后所有数会被乘多少,以及对加法操作的贡献

    (分 别记其为 mul_{i} 和 dp_{i})

    (mul_{i} 可以dfs直接计算出来(因为是一个DAG))

    (然后我们思考如何计算 dp_{i})

    首先我们必须从后往前计算 因为事实上能影响一个加法的只有其后所有的乘法操作

    • (对于操作一而言 dp_{i} = dp_{i} + mul(mul为其后所有的乘法操作))

    • (对于操作二而言 dp_{i} 无需计算,直接更新mul *= val_{i})

    • $对于操作三而言 不管它有多少次调用操作一和操作二 我们都只计算它当下的贡献 也就是 dp_{i} = dp_{i} + mul ,mul *= mul_{i} $

    (想必大家都会有疑问 对于操作三 它明明有可能调用一二两种操作 为什么只计算当下(或者说后面对它的贡献) 不要着急)

    (我们先想想我们这么做的答案是什么 每个数应该是它总共被乘上了 mul 加上某个数(这个数是加法操作和多次乘法操作的产物(雾)))

    $我们不妨就记每个数加上的数是 add_{i} $

    (然后我们按照拓扑序 依次更新 add_{i} 对于操作一 add_{i} = add_{i} + dp_{i} * val_{i};)

    (对于三操作 我们更新一下真正的 dp_{i},为了好看 假设我们更新的是dp_{j},其中j是i调用的函数)

    (那么dp_{j} 等于 dp_{j} + dp_{i} 同时我们的dp_{i}也要更新 因为 我们接下来要遍历j的调用前的函数pre 而dp_{pre} = dp{pre} + dp_{i})

    (注意此时因为已经遍历了j 那么调用pre后所有数都乘上了 mul{j},所以我们必须把这个贡献算进去 也就是dp_{i} = dp{i} * mul{j})

    (这样做会把我们真正的dp_{i}更新了,我们不需要,所以我们用一个tmp 来代替dp_{i}即可 (tmp 可以理解为是后缀积 (奇奇怪怪的想法)))

    $然后这样我们就把所有数的add_{i} 维护好了 直接输出我们的答案 val_{i} * mul + add_{i} 即可 $

    仔细想想 似乎这道题也并非那么难?

    代码如下

    
    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #include <cstdio>
    #include <queue> 
    
    using namespace std;
    
    
    const int N = 100010;
    const int mod = 998244353; 
    #define LL long long
    int n,m,x;
    int a[N];
    int p[N],val[N];
    int t[N],c;
    int func[N],vis[N],d[N];
    
    LL mu = 1,mul[N],dp[N],add[N];
    vector <int> e[N];
    queue <int> q;
    
    inline int read()
    {
    	char ch = getchar();int f = 0,x = 0;
    	while(ch > '9' || ch < '0') f |= (ch == '-'),ch = getchar();
    	while(ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (ch - '0'),ch = getchar();
    	if(f) x = -x;return x; 
    }
    
    
    void dfs(int u)
    {
    	vis[u] = true;
    	mul[u] = (t[u] == 2 ? val[u] : 1);
    	for(int j : e[u])
    	{
    		if(!vis[j]) dfs(j);
    		mul[u] = mul[j] * mul[u] % mod;
    	}
    	
    }
    
    
    signed main()
    {
    	n = read();
    	for(register int i = 1; i <= n; ++ i) a[i] = read();
    	m = read();
    	for(register int i = 1; i <= m; ++ i)
    	{
    		t[i] = read();
    		if(t[i] == 1) p[i] = read(),val[i] = read();
    		else if(t[i] == 2) val[i] = read();
    		else 
    		{
    		    c = read();
    		    while(c --)
    		    {
    		        x = read();
    		        e[i].push_back(x);
    		        d[x] ++;
    			}
    		}
    	}
    	c = read();
    	for(register int i = 1; i <= c; ++ i) func[i] = read();
    	for(register int i = 1; i <= m; ++ i) if(!vis[i] && !d[i]) dfs(i);
        
        for(register int i = c; i >= 1; -- i)
        {
        	int f = func[i];
        	if(t[f] == 1)  dp[f] = (dp[f] + mu);
        	else if(t[f] == 2) mu = (mu * val[f]) % mod;
        	else dp[f] = (dp[f] + mu), mu = (mu * mul[f]) % mod;
    	}
    	for(register int i = 1; i <= m; ++ i) 
    	  if(!d[i]) q.push(i);
    	while(q.size())
    	{
    		int hh = q.front();q.pop();
    		if(t[hh] == 1) add[p[hh]] = (add[p[hh]] + dp[hh] * val[hh]) % mod;
    	    LL tmp = dp[hh];reverse(e[hh].begin(),e[hh].end());
    	    for(int j:e[hh])
    	    {
    	    	d[j] --;if(!d[j]) q.push(j);
    	    	dp[j] = (dp[j] + tmp) % mod,tmp = tmp * mul[j] % mod;
    		}
    	}
    	for(register int i = 1; i <= n; ++ i)cout<<(a[i] * mu + add[i]) % mod<<" ";
    	return 0; 
    	
    }
    
    
  • 相关阅读:
    【转载】常考算法模板
    NOIP2020微信步数暴力80分
    NOIP2020移球游戏快速排序满分程序
    第一场NOI Online能力测试入门组B跑步
    【转】STL之Set——插入元素、二分查找元素(log级别)
    [转载]图论500题
    差分约束系统简单介绍(入门)
    辗转相除法的证明
    并查集2个优化——按秩合并和路径压缩
    递推算法之平面分割问题总结
  • 原文地址:https://www.cnblogs.com/yjyl0098/p/15437048.html
Copyright © 2011-2022 走看看