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;
    }
  • 相关阅读:
    CodeForces 19D Points (线段树+set)
    FZU 2105 Digits Count
    HDU 5618 Jam's problem again(三维偏序,CDQ分治,树状数组,线段树)
    HDU 5634 Rikka with Phi (线段树)
    Java实现 蓝桥杯 算法提高 转圈游戏(暴力快速幂)
    Java实现 蓝桥杯 算法提高 转圈游戏(暴力快速幂)
    Java实现 蓝桥杯 算法提高 转圈游戏(暴力快速幂)
    Java实现 蓝桥杯 算法提高VIP Substrings(暴力)
    Java实现 蓝桥杯 算法提高VIP Substrings(暴力)
    Java实现 蓝桥杯 算法提高VIP Substrings(暴力)
  • 原文地址:https://www.cnblogs.com/HarryPotter-fan/p/14005216.html
Copyright © 2011-2022 走看看