明天就是CSP-S了 今天打算复习下板子 把这道T3题解顺便写一下吧
题目大意是有三种函数
- 给数据中指定元素加上一个值
- 给数据所有元素都乘上一个值
- 调用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;
}