[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; 
	
}

上一篇:Python安全工具编写-pcap流量包重放


下一篇:#矩阵乘法#洛谷 3702 [SDOI2017]序列计数