zoukankan      html  css  js  c++  java
  • Luogu P4957 [COCI2017-2018#6] Alkemija

    题意

    (n) 种已知物质,现在手上有 (m) 种,每种无限多个。已知 (k) 种反应,每种可以将一些反应物变成一些生成物。求经过这些反应过后最多可以有多少种不同的物质。

    ( exttt{Data Range:}1leq mleq nleq 10^5,1leq kleq 10^5)

    题解

    由于考场上不会写各种暴力所以来练习一下如何写优雅的暴力。

    首先注意到如果所有反应都是化合或分解的话就是建个图 DFS 一下就好了,但是有多变多的就不好做。

    但是我们要有梦想。有一个非常暴力的方法是不断进行 (1sim n) 的所有反应,如果在一轮所有反应进行过后并不能使得物质种类数增加那么就认为我们得出了答案。

    这个东西效率比较低下,于是考虑怎么优化。注意到如果已经进行了某个反应的话那么以后就再也不用进行了,因为再做还是只能得到那些生成物,相当于没用,所以一个反应最多进行一次。

    不仅如此,这里还有第二个优化:考虑记录一下每个物质能参加哪些反应,这样当取出一个生成物的时候就能很快的知道这个物质可以参加哪些反应而不是 (O(k)) 去找。

    这里还有一个优化,配合第二个优化能跑得很快,就是可以不需要直接记录每个反应需要哪些物质,而是记录要完成这个反应还需要多少种物质,因为有第二个优化所以取出生成物的时候可以直接减 (1),如果这个反应不需要物质的话那么就一定可以反应了。

    最后,这 (k) 个反应的依赖顺序组成了一个 DAG,所以可以用拓扑排序的思路来更新这些反应,于是就做完了。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef int ll;
    typedef long long int li;
    const ll MAXN=2e5+51;
    queue<ll>q;
    vector<ll>re[MAXN],g[MAXN];
    ll n,m,kk,x,top,res;
    ll vis[MAXN],visr[MAXN],l[MAXN],r[MAXN],need[MAXN];
    inline ll read()
    {
        register ll num=0,neg=1;
        register char ch=getchar();
        while(!isdigit(ch)&&ch!='-')
        {
            ch=getchar();
        }
        if(ch=='-')
        {
            neg=-1;
            ch=getchar();
        }
        while(isdigit(ch))
        {
            num=(num<<3)+(num<<1)+(ch-'0');
            ch=getchar();
        }
        return num*neg;
    }
    int main()
    {
        n=read(),m=read();
        for(register int i=1;i<=m;i++)
        {
            vis[read()]=1;
        }
        kk=read();
        for(register int i=1;i<=kk;i++)
        {
            l[i]=read(),r[i]=read();
            for(register int j=1;j<=l[i];j++)
            {
                !vis[x=read()]?re[x].push_back(i),need[i]++:1;
            }
            for(register int j=1;j<=r[i];j++)
            {
                g[i].push_back(read());
            }
            !need[i]?q.push(i),visr[i]=1:1;
        }
        while(!q.empty())
        {
            top=q.front(),q.pop();
            for(register int i:g[top])
            {
                if(vis[i])
                {
                    continue;
                }
                vis[i]=1;
                for(register int j:re[i])
                {
                    !visr[j]&&!(--need[j])?q.push(j),visr[j]=1:1;
                }
            }
        }
        for(register int i=1;i<=n;i++)
        {
            res+=vis[i];
        }
        printf("%d
    ",res);
        for(register int i=1;i<=n;i++)
        {
            vis[i]?printf("%d ",i):1;
        }
    }
    
  • 相关阅读:
    Manjaro mirror in china
    gnome3.X添加开机启动项
    ssh无密钥登陆的简单配置
    ESXi虚拟磁盘共享
    记录一次fat32格式U盘不识别问题
    更换内核后重编virtualbox内核模块
    宏定义字符串连接和符号粘贴
    kernel source reading notepad
    linux kernel tainted
    设计模式-观察者模式(Observer Pattern)
  • 原文地址:https://www.cnblogs.com/Karry5307/p/13881597.html
Copyright © 2011-2022 走看看