95分求助

P7077 [CSP-S2020] 函数调用

zixiangzhan @ 2021-09-19 23:00:10

#include<bits/stdc++.h>
using namespace std;
#define int long long 
#define LL long long
#define il inline
#define re register
il int read()
{
    int s=0,w=1;char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-') w=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        s=(s<<1)+(s<<3)+c-'0';
        c=getchar();
    }
    return s*w;
}
il LL read_ll()
{
    LL s=0,w=1;char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-') w=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        s=(s<<1)+(s<<3)+c-'0';
        c=getchar();
    }
    return s*w;
}
const int N=100010;
const int M=1000010;
const LL mod=998244353;
int n,m,Q,op,rt;
LL a[N];
LL ad[N],mul[N],cnt[N];
int pos[N];
int zhan[N],n_zh;
int head1[N],ver1[M],nxt1[M],tot1,ru1[N];
int head2[N],ver2[M],nxt2[M],tot2,ru2[N];
queue<int> q;
/*
NOTE
ad:"add",对应某一1操作的V值 
mul:multiply,即调用这个函数后整个序列变成原来的几倍
cnt:某一函数被调用多少次 
要建立超级节点 
pos:1操作的P ,即1操作对应得下标 
"1""2"分别表示topo1,topo2使用的图,即分别为
反图/正图对应的变量 
(雀食十分别扭) 
*/ 
il void kkk1(int u,int v)
{
    ver1[++tot1]=v;
    nxt1[tot1]=head1[u];
    head1[u]=tot1;
}
il void kkk2(int u,int v)
{
    ver2[++tot2]=v;
    nxt2[tot2]=head2[u];
    head2[u]=tot2;
}
il void topo1()
{
    while(!q.empty()) q.pop();
    for(re int i=1;i<=rt;i++){
        if(ru1[i]==0) q.push(i);//!
    }
    while(q.size()){
        int xx=q.front();
        q.pop();
        for(re int i=head1[xx];i;i=nxt1[i]){
            int vv=ver1[i];
            mul[vv]=(mul[vv]*mul[xx])%mod;
            //跑反图的过程就相当于dfs回溯了,所以其实
            //记忆化搜索也行 
            ru1[vv]--;
            if(ru1[vv]==0) q.push(vv);
        }
    }
}
il void topo2()
{//正图反着跑可还行…… 
    while(!q.empty()) q.pop();
    for(re int i=1;i<=rt;i++){ 
        if(!ru2[i]) q.push(i);
    }
    while(q.size()){
        int xx=q.front();
        q.pop();
        LL now_mul=1LL;
        //当前函数内的乘法标记 
        for(re int i=head2[xx];i;i=nxt2[i]){
            /*这里注意要倒序遍历,因为每个函数执行完之后
            只影响在它前面执行的函数,而由于链表特性,
            正着挨个插进去之后,后插入的正好就在前面
            虽然这里考不考虑对结果没有影响,但是应该
            考虑这个问题*/ 
            int vv=ver2[i];
            cnt[vv]=(cnt[vv]+cnt[xx]*now_mul)%mod;
            now_mul=(now_mul*mul[vv])%mod;
            ru2[vv]--;
            if(ru2[vv]==0) q.push(vv); 
        }
    }
    return;
}
signed main()
{
    freopen("P7077_20.in","r",stdin);
    freopen("lz_P7077.out","w",stdout); 
    n=read();
    for(re int i=1;i<=n;i++) a[i]=read_ll();
    m=read(); 
    rt=m+1;//超级根节点,找点的时候一定不要忘了把超级根节点也
    //考虑到!!! 
    mul[rt]=1,cnt[rt]=1;
    for(re int i=1;i<=m;i++){
        op=read();
        if(op==1){
            pos[i]=read(),ad[i]=read(),mul[i]=1;
        }
        else if(op==2) mul[i]=read_ll();
        else if(op==3){
            mul[i]=1;
            ru1[i]=read();
            for(re int j=1;j<=ru1[i];j++){
                int vv=read();
                kkk2(i,vv);
                ru2[vv]++;
                zhan[++n_zh]=vv;
            }
            while(n_zh){
                kkk1(zhan[n_zh],i);
                zhan[n_zh--]=0;
            }
        }
    }
    Q=read();
    for(re int i=1;i<=Q;i++){
        int vv=read();
        kkk2(rt,vv);
        ru2[vv]++;
        zhan[++n_zh]=vv;
    }
    ru1[rt]=Q;
    while(n_zh){
        kkk1(zhan[n_zh],rt);
        zhan[n_zh--]=0;
    }
    topo1();//第一遍跑反图 
    topo2();//第二遍跑正图 
    for(re int i=1;i<=n;i++) a[i]=(a[i]*mul[rt])%mod;
    //因为每一次乘法操作都对原始序列有影响,而topo2并没
    //有进行这个操作 
    for(re int i=1;i<=m;i++){
        if(ad[i]!=0) a[pos[i]]=(a[pos[i]] + (cnt[i]*ad[i]%mod))%mod;
    }
    for(re int i=1;i<=n;i++){
        printf("%lld ",((a[i]%mod)+mod)%mod);
    }
    return 0;
}

by zixiangzhan @ 2021-09-19 23:01:00

最后一个点WA掉了,而且差的很离谱


by peiwenjun @ 2021-09-29 11:16:42

数组开大一点,比如把M调成2e6,我自己开1e6的数组也是莫名其妙TLE(O2下WA),然后开成2e6就A了


by zixiangzhan @ 2021-10-01 22:42:39

@peiwenjun 真的欸!谢谢您(数组真是个玄学的东西


by peiwenjun @ 2021-10-02 08:40:27

不用谢


|