Fiolki

时间限制: 3 Sec  内存限制: 128 MB

题目描述

化学家吉丽想要配置一种神奇的药水来拯救世界。

吉丽有n种不同的液体物质,和n个药瓶(均从1到n编号)。初始时,第i个瓶内装着g[i]克的第i种物质。吉丽需要执行一定的步骤来配置药水,第i个步骤是将第a[i]个瓶子内的所有液体倒入第b[i]个瓶子,此后第a[i]个瓶子不会再被用到。瓶子的容量可以视作是无限的。

吉丽知道某几对液体物质在一起时会发生反应产生沉淀,具体反应是1克c[i]物质和1克d[i]物质生成2克沉淀,一直进行直到某一反应物耗尽。生成的沉淀不会和任何物质反应。当有多于一对可以发生反应的物质在一起时,吉丽知道它们的反应顺序。每次倾倒完后,吉丽会等到反应结束后再执行下一步骤。

吉丽想知道配置过程中总共产生多少沉淀。

输入

第一行三个整数n,m,k(0<=m<n<=200000,0<=k<=500000),分别表示药瓶的个数(即物质的种数),操作步数,可以发生的反应数量。

第二行有n个整数g[1],g[2],…,g[n](1<=g[i]<=10^9),表示初始时每个瓶内物质的质量。

接下来m行,每行两个整数a[i],b[i](1<=a[i],b[i]<=n,a[i]≠b[i]),表示第i个步骤。保证a[i]在以后的步骤中不再出现。

接下来k行,每行是一对可以发生反应的物质c[i],d[i](1<=c[i],d[i]<=n,c[i]≠d[i]),按照反应的优先顺序给出。同一个反应不会重复出现。

输出

配置过程中总共产生多少沉淀。

样例输入

3 2 1
2 3 4
1 2
3 2
2 3

样例输出

6


题解
        题目并不复杂,而且有很多可以利用的性质,比如说每个反应只会进行一次,所有物质在遇到反应物之前并没有什么用之类的,但是还是只想到了n^2的并查集+链表做法,水了30。通过这道题学会了一种很神奇的东西叫重构树,即每倾倒一次就新建一个节点作为这两个节点的父亲,然后以此类推,可以想见每一个反应一定是在LCA上发生的,然后我们就可以欢快地按优先级处理每个点发生的反应,只要倒着加边反应就会按优先级从大到小进行。重构树作为一种新思路让这道题一下子简明了起来,不过我实现得有点繁冗,因为用的是离线LCA,再加上把每个反应倒序加到LCA上一共用了三个邻接表两个并查集。

 

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int sj1=200005;
const int sj2=500005;
const int sj3=sj1+sj2;
int n,m,k,f[sj3],g[sj1],yz[sj2],yy[sj2],h[sj1],zx[sj3];
int tp,d[sj3],e1,e2,lca[sj2],fa[sj2],size,w[sj3];
long long ans;
bool r[sj3];
int read()
{
    int jg=0,jk=getchar()-'0';
    if(jk<=9&&jk>0) jg=jk;
    jk=getchar()-'0';
    while(jk<=9&&jk>=0)
       jg=jg*10+jk,jk=getchar()-'0';
    return jg;
}
int find(int x)
{
    if(f[x]==-1) return x;
    f[x]=find(f[x]);
    return f[x];
}
void hb(int x,int y)
{
    x=find(x),y=find(y);
    if(x!=y) f[x]=y;
}
int fi(int x)
{
    if(w[x]==-1) return x;
    w[x]=fi(w[x]);
    return w[x];
}
void hbb(int x,int y)
{
     x=fi(x),y=fi(y);
     if(x!=y) w[x]=y;
}
struct tree
{
    int v,ne,num;     
}t[sj2<<1];
struct B
{
    int v,ne;
}b[sj2<<2];
void ad1(int x,int y,int z)
{
     t[e1].v=y,t[e1].ne=h[x];
     t[e1].num=z,h[x]=e1++;
}
void ad2(int x,int y)
{
     b[e2].v=y,b[e2].ne=d[x];
     d[x]=e2++;
}
void init()
{
     memset(d,-1,sizeof(d));
     memset(h,-1,sizeof(h));
     memset(f,-1,sizeof(f));
     memset(w,-1,sizeof(w));
     n=read(),m=read(),k=read();
     size=n;
     for(int i=1;i<=n;i++)  g[i]=read();
     for(int i=1;i<=m;i++) 
     {
       tp=read(),e1=read();
       size++;
       ad2(find(tp),size),ad2(find(e1),size);
       ad2(size,find(tp)),ad2(size,find(e1));
       hb(tp,size),hb(e1,size);
     }
     for(int i=1;i<=k;i++)  yz[i]=read(),yy[i]=read();
     e1=0;
     for(int i=k;i>=1;i--)
       ad1(yz[i],yy[i],i),ad1(yy[i],yz[i],i);
}
void tarjan(int x)
{
     r[x]=1,zx[x]=x;
     for(int i=d[x];i!=-1;i=b[i].ne)
     {
       if(!r[b[i].v])
       {
         tarjan(b[i].v);
         hb(x,b[i].v);
         zx[find(b[i].v)]=x;
       }
       hbb(x,b[i].v);
     }
     if(x<=n)
       for(int i=h[x];i!=-1;i=t[i].ne)
         if(r[t[i].v]&&fi(x)==fi(t[i].v))
           fa[t[i].num]=zx[find(t[i].v)];
}
int main()
{
    init();
    memset(f,-1,sizeof(f));
    for(int i=n+m;i>=1;i--)
      if(!r[i])  tarjan(i);
    memset(b,0,sizeof(b));
    memset(d,-1,sizeof(d));
    for(int i=k;i>=1;i--)
      if(fa[i])
        ad2(fa[i],i);
    for(int i=n+1;i<=n+m;i++)
      for(int j=d[i];j!=-1;j=b[j].ne)
      {
         tp=b[j].v;
         if(g[yy[tp]]==0||g[yz[tp]]==0) continue;
         if(g[yy[tp]]<=g[yz[tp]])
         {
            ans+=g[yy[tp]];
            g[yz[tp]]-=g[yy[tp]],g[yy[tp]]=0;
         }
         else
         {
            ans+=g[yz[tp]];
            g[yy[tp]]-=g[yz[tp]],g[yz[tp]]=0;
         }
      }
    printf("%lld",ans<<1);
    return 0;
}
fio
 

转载于:https://www.cnblogs.com/moyiii-/p/7500219.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值