zoukankan      html  css  js  c++  java
  • CSP 2020 T3 函数调用

    description

    solution:

    总的来说有三种操作

    • 单点加
    • 区间乘
    • 依次进行的函数调用

    显然最麻烦的是3操作
    容易发现所有3操作形成一个(DAG),我们考虑如何再(DAG)上操作(容易发现出度为(0)的点都是1操作或2操作)
    首先有一个神奇的想法:对于每次加法操作(假设加上(v)),我们只要求出它的后缀积(设为(mul))那么最后相当于加上了(v imes mul)
    于是我们可以先求出每个点对应的积
    然后离线下所有修改,从后往前扫描,不断累乘后缀积作为当前节点的(tag)
    然后在(DAG)上按照拓扑序依次下放标记,具体来说:

    • 当前节点为1操作,那么在对应位置加上值(*)后缀积即可
    • 当前节点为2操作,直接忽略
    • 当前节点为3操作,逆序遍历它指向的点(链式前向星自带逆序),下传标记后乘上当前遍历到的点对应的积

    然后这道我认为本次考试出的最好的题目就结束了

    code:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5+5,mod=998244353;
    int n,m,q,tot,a[N],opt[N],p[N],v[N],d[N],qu[N];
    int fi[N],ne[N<<4],to[N<<4],tops[N],deg[N],mul[N],tag[N];
    inline void adde(int x,int y)
    {
    	ne[++tot]=fi[x],fi[x]=tot,to[tot]=y;
    }
    struct cmp{bool operator()(const int&x,const int&y){return tops[x]<tops[y];}};
    inline int add(int x,int y){x+=y;return x>=mod?x-mod:x;}
    inline void pre()
    {
    	for(int i=1;i<=tot;++i)++deg[to[i]];
    	queue<int>q;
    	for(int i=1;i<=m;++i)if(!deg[i])q.push(i),tops[i]=1;
    	while(!q.empty())
    	{
    		int u=q.front();q.pop();
    		for(int i=fi[u];i;i=ne[i])
    		{
    			int v=to[i];
    			if(!(--deg[v]))q.push(v),tops[v]=tops[u]+1;
    		}
    	}
    	for(int i=1;i<=m;++i)d[i]=i;
    	sort(d+1,d+m+1,cmp());
    	for(int i=m;i>=1;--i)
    	{
    		int u=d[i];
    		mul[u]=1;
    		if(opt[u]==2)mul[u]=v[u];
    		for(int j=fi[u];j;j=ne[j])
    		{
    			int v=to[j];
    			mul[u]=1ll*mul[u]*mul[v]%mod;
    		}
    	}
    }
    inline void solve()
    {
    	for(int i=1;i<=m;++i)
    	{
    		int u=d[i];
    		if(opt[u]==1)a[p[u]]=add(a[p[u]],1ll*v[u]*tag[u]%mod);
    		for(int j=fi[u];j;j=ne[j])
    		{
    			int v=to[j];
    			tag[v]=add(tag[v],tag[u]);
    			tag[u]=1ll*tag[u]*mul[v]%mod;
    		}
    	}
    }
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;++i)scanf("%d",a+i);
    	scanf("%d",&m);
    	for(int i=1;i<=m;++i)
    	{
    		scanf("%d",opt+i);
    		if(opt[i]==1)scanf("%d%d",p+i,v+i);
    		else if(opt[i]==2)scanf("%d",v+i);
    		else 
    		{
    			int num;scanf("%d",&num);
    			while(num--){int x;scanf("%d",&x);adde(i,x);}
    		}
    	}
    	pre();int t=1;
    	scanf("%d",&q);
    	for(int i=1;i<=q;++i)scanf("%d",qu+i);
    	for(int i=q;i>=1;--i)
    	{
    		int u=qu[i];
    		if(opt[u]==1)tag[u]=add(tag[u],t);
    		else if(opt[u]==2)t=1ll*t*mul[u]%mod;
    		else tag[u]=add(tag[u],t),t=1ll*t*mul[u]%mod;
    	}
    	for(int i=1;i<=n;++i)a[i]=1ll*a[i]*t%mod;
    	solve();
    	for(int i=1;i<=n;++i)printf("%d ",a[i]);
    	return 0;
    }
    
  • 相关阅读:
    MyBatis 智能标签
    MyBatis入门
    Hibernate 分组查询 子查询 原生SQL
    组件映射
    Hibernate组件映射
    hibernate 中的 lazy=”proxy” 和 lazy=”no-proxy” 的区别
    save(),saveOrUpdate(),merge()的区别
    ThreadLocal
    OID,主键生成策略,PO VO DTO,get和load区别,脏检查,快照,java对象的三种状态
    HIbernate实现增、删、改、查。
  • 原文地址:https://www.cnblogs.com/zmyzmy/p/13955459.html
Copyright © 2011-2022 走看看