CSP-2020 T3 函数调用
总结
在考场上因为看到这题时没多少时间了,草草的看了下题,就开始了数据结构暴力搞,后来发现要在递归里套递归,就放弃了(逃)。考后在吸收了他人的经验之后,前来补上。
这题刚看有点像数据结构,但经过仔细分析后,其实不是。它只需要单点修改和区间乘(如果真是数据结构,不至于这样);
如果我们把每个操作看作一个点,把每个类型为\(3\)的操作与它之后要调用的函数连一条边,再加上题目说了不会之间或间接的调用自己,即无环,那么它就是一个\(DAG\)。对于\(DAG\),那就有很多可以操作的了。
从题目入手,若操作为类型\(1\),及只需对某一个元素进行修改即可,若在它之后有类型为\(2\)的操作,将整个数列乘上\(k\),那么可以理解为这个类型为\(1\)的操作被执行了\(k\)次。那么,我们需要求出每个单点加的操作之后有多少个乘的操作,共乘了多少倍,记为\(sum\),仔细想一下,倒着来会好一些,因为我们不可能对于每个操作都单独去求一次\(sum\),所以肯定是一遍从后往前求,因为你后面求\(sum\)的对于前面的而言,也是需要的,前面求的又不会影响到你后面的。
至于顺序,就按着它给出的操作顺序倒着来。
然后对于每个操作在执行后会有多少乘积累加,我们用用拓扑序倒序来求,反正它都已经是个\(DAG\)了,记为\(mul\),在求\(sum\)前求出来。
不过需要注意的是,在你求出\(mul\)和\(sum\)之后,还需要将\(sum\)下放,
就拿这个图来说,我们假设\(1\)节点的\(sum\)为\(x\),那么\(+2\)这个操作的\(sum\)应格外增加\(3x\),同理\(+1\)的\(sum\)应增加\(12x\)。(好像没搞很懂)
所以下传\(sum\)时,假设一个节点\(x\)的\(sum\)为\(S\),儿子为\(y\),从\(1\)到\(k\)编号,那么\(y_i\)的\(sum\)就应该增加
最后遍历一遍即可。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+7;
const int mod=998244353;
#define NEKO puts("NEKO")
#define ll long long
#define il inline
#define vocaloid(v) (v>='0'&&v<='9')
template <typename T>
il void read(T &x)
{
x=0;int flag=1;char v=getchar();
while(!vocaloid(v)) {if(v=='-') flag=-1;v=getchar();}
while(vocaloid(v)) {x=(x<<1)+(x<<3)+v-'0';v=getchar();}
x*=flag;
}
template <typename T>
il void write(T x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
struct miku{
int v,next;
}MIKU[maxn<<1];
struct Alone{
int tp,pos;
ll mul,sum,val;
}b[maxn];
ll a[maxn];
int n,m,Q,cnt,ord[maxn];
int h[maxn],in[maxn],opt[maxn],rin;
queue<int>q;
il void add(int u,int v)
{
MIKU[++cnt].next=h[u];
MIKU[cnt].v=v;
h[u]=cnt;
}
il void topo()
{
for(int i=1;i<=m;i++)
if(!in[i]) q.push(i);
while(!q.empty())
{
int x=q.front();q.pop();
opt[++rin]=x;
for(int i=h[x];i;i=MIKU[i].next)
{
int v=MIKU[i].v;
in[v]--;
if(!in[v]) q.push(v);
}
}
}
il void dfs()
{
for(int i=m;i>=1;i--)
{
int x=opt[i];
for(int j=h[x];j;j=MIKU[j].next)
{
int v=MIKU[j].v;
b[x].mul=b[x].mul*b[v].mul%mod;
}
}
}
il void down()
{
for(int i=1;i<=m;i++)
{
int x=opt[i];ll now=1;
for(int j=h[x];j;j=MIKU[j].next)
{
int v=MIKU[j].v;
b[v].sum=(b[v].sum+b[x].sum*now%mod)%mod;
now=now*b[v].mul%mod;
}
}
}
int main()
{
read(n);
for(int i=1;i<=n;i++) read(a[i]);
read(m);
for(int i=1;i<=m;i++)
{
read(b[i].tp);
if(b[i].tp==1)
read(b[i].pos),read(b[i].val),b[i].mul=1;
else if(b[i].tp==2)
read(b[i].val),b[i].mul=b[i].val;
else
{
int x;read(b[i].pos);b[i].mul=1;
for(int j=1;j<=b[i].pos;j++)
{
read(x);add(i,x);
in[x]++;
}
}
}
topo();dfs();
read(Q);ll now=1;
for(int i=1;i<=Q;i++) read(ord[i]);
for(int i=Q;i>=1;i--)
{
int x=ord[i];b[x].sum=(b[x].sum+now)%mod;
now=now*b[x].mul%mod;
}
down();
for(int i=1;i<=n;i++) a[i]=a[i]*now%mod;
for(int i=1;i<=m;i++)
if(b[i].tp==1)
a[b[i].pos]=(a[b[i].pos]+b[i].val*b[i].sum%mod)%mod;
for(int i=1;i<=n;i++) write(a[i]),printf(" ");
return 0;
}