Luogu P7077 CSP-S2020 函数调用 题解 [ 蓝 ] [ 拓扑排序 ] [ 动态规划 ] [ 数学 ]

函数调用:个人非常喜欢的一道拓扑题。

转化

这题一共有三种操作,不太好搞。而第一个函数看起来就比较可做,第三个函数显然就是让你拓扑转移,于是我们考虑第二个操作怎么处理。

当我们进行一个操作一后,假设当前节点的增量为 \(ad\)。此时如果再进行一次操作二,全局乘上 \(mul\),那么该节点最终的增量为 \(ad\times mul\)。根据乘法的定义,我们可以把这个增量转化为对该节点进行 \(mul\) 次操作一。

这就启发了我们考虑每个操作一会被执行多少次,显然是这个操作后面的操作的后缀积。

于是这题最关键的一步转化就做完了。

拓扑

因为操作三是要依次执行操作,也就是说它的子操作执行时,乘上的操作次数可能不相同。

因此,我们可以先预处理出每个操作完成之后会给全局乘上什么数,以便后面的计算。这可以通过倒着拓扑一遍处理。

接下来就要转移函数执行次数了,我们定义 \(dp_i\) 表示该操作要被执行的次数。

显然,我们可以倒着枚举执行的操作,维护该操作内部的后缀积 \(nmul\),进行如下转移:

\[dp_v \gets dp_v+dp_u \times nmul \]

这就保证了前面的操作一定会算上后面函数的乘积了。

那么该如何初始化呢?同样是倒序枚举它给定的操作,维护一个后缀积,处理到某个操作的初始值时,其初始值就是当前的后缀积。

最后统计答案是容易的,因为只剩下操作一了,只要我们依次累计上答案即可。注意原来的值要乘上所有操作的全局积。

时间复杂度 \(O(m)\)

实现

这题码量较大,再次梳理一遍代码过程:

  1. 倒着拓扑一遍,计算出每个函数操作之后给全局乘了多少。
  2. 倒着操作所有函数调用,实时维护一个后缀乘积,然后给各函数打标记。
  3. 顺着拓扑一遍,对于第三种函数,我们倒着枚举它的出边,然后进行转移,转移之后乘上对应节点的全局乘贡献。

代码

以上三个步骤在代码中被拆分成了三个函数来实现。

注意所有时候都要取模。

#include <bits/stdc++.h>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pi;
const ll mod=998244353;
int n,m,rd[100005],frd[100005],q,opf[100005];
ll a[100005],mul[100005],dp[100005],allmul,ad[100005];
struct node{
    int tp,p;
    ll v;
}f[100005];
vector<int>g[100005],fg[100005];
void topo1()
{
    queue<int>q;
    for(int i=1;i<=m;i++)
    {
        if(frd[i]==0)q.push(i);
        if(f[i].tp==2)mul[i]=f[i].v;
        else mul[i]=1;
    }
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(auto v:fg[u])
        {
            mul[v]=(mul[v]*mul[u])%mod;
            frd[v]--;
            if(frd[v]==0)q.push(v);
        }
    }
}
void init()
{
    ll nmul=1;
    for(int i=q;i>=1;i--)
    {
        dp[opf[i]]=(dp[opf[i]]+nmul)%mod;
        nmul=(nmul*mul[opf[i]])%mod;
    }
    allmul=nmul;
}
void topo2()
{
    queue<int>q;
    for(int i=1;i<=m;i++)if(rd[i]==0)q.push(i);
    ll nmul=1;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        nmul=1;
        for(int i=g[u].size()-1;i>=0;i--)
        {
            int v=g[u][i];
            dp[v]=(dp[v]+dp[u]*nmul%mod)%mod;
            nmul=(nmul*mul[v])%mod;
            rd[v]--;
            if(rd[v]==0)q.push(v);
        }
    }
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    cin>>m;
    for(int i=1;i<=m;i++)
    {
        int tp,p,v;
        cin>>tp;
        if(tp==1)
        {
            cin>>p>>v;
            f[i]={1,p,v};
        }
        else if(tp==2)
        {
            cin>>v;
            f[i]={2,0,v};
        }
        else
        {
            cin>>p;
            for(int j=1;j<=p;j++)
            {
                int to;
                cin>>to;
                g[i].push_back(to);
                rd[to]++;
                fg[to].push_back(i);
                frd[i]++;
            }
            f[i]={3,p,0};
        }
    }
    cin>>q;
    for(int i=1;i<=q;i++)cin>>opf[i];
    topo1();
    init();
    topo2();
    for(int i=1;i<=m;i++)if(f[i].tp==1)ad[f[i].p]=(ad[f[i].p]+dp[i]*f[i].v%mod)%mod;
    for(int i=1;i<=n;i++)cout<<(allmul*a[i]%mod+ad[i])%mod<<" ";
    return 0;
}
posted @ 2024-12-27 00:31  KS_Fszha  阅读(6)  评论(0编辑  收藏  举报