[CSP-S2020] 函数调用
题目传送门
比较绕一点。
貌似数据结构,实则不好维护。
假如说不考虑3操作,维护一个加标记和乘标记是好做的。注意到不会递归,于是把所有的3操作向他要执行的操作连边,形成了一个 DAG 。
对每个操作维护一个乘系数\(tag\),表示执行该操作后全体要乘上的值,显然对于1操作,\(tag\)为\(1\),对于2操作,\(tag\)为该操作要乘的数,对于3操作,\(tag\)为他要执行的所有操作(也即 DAG 中他直接连向的所有点)之积。
拓扑排序以后,倒着扫一遍即可求出所有\(tag\)。
\(tag\)是单次操作的乘系数,需要考虑再给定操作序列中的计算。记每个操作在全局中的乘系数为\(final\)。
注意到对于每个加操作,只有在他之后的乘操作才会对他产生影响,于是倒着做,记录一个\(bac\)表示后缀的乘系数之积,我们令:
\(final_i=final_i+bac(i:Q\to1)\)。
为什么要加?因为一个操作\(x\)如果出现不止一次,那么\(x\times bac+x\times bac'=x\times (bac+bac')\)。
除此之外,由于所有3操作等价于再执行一遍某些1,2操作,因此3操作的\(final\)需要向他所连的点传递。传递时在每个3操作内部仍然是倒着做,过程与上面类似,因为一个3操作内部也会有加和乘。
事实上,上述过程把所有操作向加操作转换,最后只需:
\(a_i=a_i\times bac_1\)
\(a_{p_{x_i}}=a_{p_{x_i}}+v_{x_i}\times final_{x_i}\ (type_{x_i}=1)\)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pp pop_back
#define pb push_back
#define ins insert
#define lowbit(x) x & -x
const int N = 6e5 + 10;
const int mod = 998244353;
const ll INF = 1e18;
int read(){int x; scanf("%d", &x); return x; }
ll readll(){ll x; scanf("%lld", &x); return x; }
int n, m, Q;
int tops[N], ind[N], s[N];
ll a[N];
struct node{
int type, pos, tt;
ll tag, final, v;
}p[N];
vector<int>l[N];
queue<int>q;
signed main(){
n = read();
for(int i = 1; i <= n; i++) a[i] = readll();
m = read();
for(int i = 1; i <= m; i++){
p[i].type = read();
if(p[i].type == 1){
p[i].pos = read(), p[i].v = read();
p[i].tag = 1;
} else if(p[i].type == 2){
p[i].v = read();
p[i].tag = p[i].v;
} else {
p[i].tt = read();
for(int j = 1; j <= p[i].tt; j++){
int id = read();
l[i].pb(id);
ind[id]++;
}
p[i].tag = 1;
}
}
for(int i = 1; i <= m; i++) if(! ind[i]) q.push(i);
int cnt = 0;
while(q.size()){
int x = q.front();
q.pop();
tops[++cnt] = x;
for(auto y : l[x]){
if(--ind[y] == 0) q.push(y);
}
}
for(int i = m; i >= 1; i--){
int x = tops[i];
for(auto y : l[x]) (p[x].tag *= p[y].tag) %= mod;
}
Q = read();
for(int i = 1; i <= Q; i++) s[i] = read();
ll bac = 1;
for(int i = Q; i; i--){
int x = s[i];
(p[x].final += bac) %= mod;
(bac *= p[x].tag) %= mod;
}
for(int i = 1; i <= n; i++) (a[i] *= bac) %= mod;
for(int i = 1; i <= m; i++){
int x = tops[i];
ll Bac = p[x].final;
for(int i = l[x].size() - 1; i >= 0; i--){
int y = l[x][i];
(p[y].final += Bac) %= mod;//额外做一次,所以加上后面乘的
(Bac *= p[y].tag) %= mod;
}
}
for(int i = 1; i <= m; i++){
if(p[i].type == 1) (a[p[i].pos] += p[i].v * p[i].final % mod) %= mod;
}
for(int i = 1; i <= n; i++) cout << a[i] << ' ';
return 0;
}
$\color{blue} \mathcal {Lordreamland}$

浙公网安备 33010602011771号