zoukankan      html  css  js  c++  java
  • [CSP2020]函数调用

    1.考虑只有类型1,2的操作的情况:

    假设一个数组依次执行:a[1]+1, *3, a[1]+2, a[3]+2, *2 

    那么 a[1] = a[1]*3*2 + 1*(3*2) + 2*2 , a[3] = a[3]*3*2 + 2*2 , 数组其余元素均乘上3*2=6倍

    发现对于乘法操作,在最后给所有数组元素乘上就好了,

    而一个加法操作带的系数就等于它后面的所有乘法操作之积

    因此可以倒序处理,从后往前记录已进行的所有乘法操作的积是多少,这样就能计算出每次加法操作带的系数是多少

    2.考虑类型3的操作:

    • 先考虑乘法:

    对于图上的每个点(代表着一种操作),维护一个mul,表示执行一次这个操作会给累计的积乘上多少

    对于1类操作,它的mul=1;对于2类操作,它的mul就等于它要乘上的值;而对于3类操作,它的mul等于它直接连向的所有点的mul之积

     

    以样例2为例,点2的mul为2,点3的mul为3,所以点1的mul为6,那么执行一次操作1就会让前面执行过的所有的加法操作再乘上6的系数

    由题目的条件易得,函数的调用关系构成一个DAG

    按照拓扑序倒序扫一遍即可处理出mul

    • 再考虑加法:

    对于每种类型1或3的操作,维护一个k,表示这个操作带着多少系数

    先倒序处理 q 次操作,按照只有类型1,2的方法处理出k,

    再把类型3的节点的k下传到它所包含的的类型1的节点,

    即可处理出最终的k

    • 注意:有些类型3的操作既包含加法又包含乘法

    为了确保答案的正确性,必须从后往前倒序处理所有调用的函数

    #include<iostream>
    #include<cstdio>
    #include<queue>
    using namespace std;
    typedef long long ll;
    const ll mod=998244353;
    const int N=1000005;
    struct Edge{
        int v,nxt;
    }edge[N];
    ll a[N];
    int n,m,Q,F[N],cnt,head[N],d[N],rnd[N],len;
    queue<int> q;
    struct func{
        int tp,p;
        ll v,mul,k;
    }b[N];
    void addedge(int u,int v){
        edge[++cnt].v=v;
        edge[cnt].nxt=head[u];
        head[u]=cnt;
        d[v]++;
    }
    void tuopu(){
        for(int i=1;i<=m;i++) 
            if(!d[i]) q.push(i);
        while(!q.empty()){
            int u=q.front();
            q.pop();
            rnd[++len]=u;
            for(int i=head[u];i;i=edge[i].nxt){
                int v=edge[i].v;
                d[v]--;
                if(!d[v]) q.push(v);
            }
        }
    }
    void getmul(){
        for(int i=m;i>=1;i--){
            int u=rnd[i];
            for(int j=head[u];j;j=edge[j].nxt){
                int v=edge[j].v;
                b[u].mul=1ll*b[u].mul*b[v].mul%mod;
            }
        }
    }
    void getk(){
        for(int i=1;i<=m;i++){
            int u=rnd[i]; 
            ll now=1;
            for(int j=head[u];j;j=edge[j].nxt){
                int v=edge[j].v;
                b[v].k=(b[v].k+b[u].k*now%mod)%mod;
                now=now*b[v].mul%mod;
            }
        }
    }
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++) 
            scanf("%lld",&a[i]);
        scanf("%d",&m);
        for(int i=1;i<=m;i++){
            scanf("%d",&b[i].tp);
            if(b[i].tp==1){
                scanf("%d%lld",&b[i].p,&b[i].v);
                b[i].mul=1;
            }
            else if(b[i].tp==2){
                scanf("%lld",&b[i].v);
                b[i].mul=b[i].v;
            }
            else{
                scanf("%d",&b[i].p);b[i].mul=1;
                for(int j=1,x;j<=b[i].p;j++){
                    scanf("%d",&x);
                    addedge(i,x);
                }
            }
        }
        tuopu();
        getmul();
        scanf("%d",&Q);
        for(int i=1;i<=Q;i++) 
            scanf("%d",&F[i]);
        ll now=1;
        for(int i=Q;i>=1;i--){
            int x=F[i]; 
            b[x].k=(b[x].k+now)%mod;
            now=now*b[x].mul%mod;
        }
        getk();
        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].p]=(a[b[i].p]+b[i].v*b[i].k%mod)%mod;
        }
        for(int i=1;i<=n;i++) 
            printf("%lld ",a[i]);
        return 0;
    }
  • 相关阅读:
    Intellij Idea 设置之方法快速显示
    HTML转码码
    MIT自然语言处理第五讲:最大熵和对数线性模型(第一部分)
    MIT自然语言处理第五讲:最大熵和对数线性模型(第二部分)
    MIT自然语言处理第五讲:最大熵和对数线性模型(第四部分)
    MIT自然语言处理第五讲:最大熵和对数线性模型(第三部分)
    文本分类专题(ultimate 版)绝对是目前最全的C++版开源文本分类代码和最令人耳目一新的实验解释
    intellij idea教程
    [转] 一个大数相乘的C/C++实现
    5个海盗分金币的问题
  • 原文地址:https://www.cnblogs.com/HarryPotter-fan/p/14005216.html
Copyright © 2011-2022 走看看