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

 
时间: 2024-10-23 17:28:39

Fiolki的相关文章

【BZOJ-3712】Fiolki LCA + 倍增 (idea题)

3712: [PA2014]Fiolki Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 303  Solved: 67[Submit][Status][Discuss] Description 化学家吉丽想要配置一种神奇的药水来拯救世界.吉丽有n种不同的液体物质,和n个药瓶(均从1到n编号).初始时,第i个瓶内装着g[i]克的第i种物质.吉丽需要执行一定的步骤来配置药水,第i个步骤是将第a[i]个瓶子内的所有液体倒入第b[i]个瓶子,此后第a[i

[PA2014]Fiolki 解题报告

这道题是delayyy拿来当例题给我们讲的,他讲得启发式合并,以外向边与点数的和为关键字(因为这是我们一次合并需要的代价),可以做到O(m+(n+k)log(n+k)) 但是..显然这是一棵树嘛,一棵树的话你直接求LCA就好了,求LCA可以Tarjan做到O(n),从哪棵子树上来的可以在ufs里维护一下,然后把所有的点对按照询问给出的顺序插入就可以代替排序的效果了.所以这样可以做到O(n+m+k),所以就很愉快的刷了Rank1! 一个容易出问题的地方是并查集维护的时候要先压缩..再维护信息.于是

[BZOJ3712]Fiolki 重构树(并查集)

3712: [PA2014]Fiolki Time Limit: 30 Sec  Memory Limit: 128 MB Description 化学家吉丽想要配置一种神奇的药水来拯救世界.吉丽有n种不同的液体物质,和n个药瓶(均从1到n编号).初始时,第i个瓶内装着g[i]克的第i种物质.吉丽需要执行一定的步骤来配置药水,第i个步骤是将第a[i]个瓶子内的所有液体倒入第b[i]个瓶子,此后第a[i]个瓶子不会再被用到.瓶子的容量可以视作是无限的.吉丽知道某几对液体物质在一起时会发生反应产生沉

Fiolki题解

问题 B: 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克

[HDU 3712] Fiolki (带边权并查集+启发式合并)

[HDU 3712] Fiolki (带边权并查集+启发式合并) 题面 化学家吉丽想要配置一种神奇的药水来拯救世界. 吉丽有n种不同的液体物质,和n个药瓶(均从1到n编号).初始时,第i个瓶内装着g[i]克的第i种物质.吉丽需要执行一定的步骤来配置药水,第i个步骤是将第a[i]个瓶子内的所有液体倒入第b[i]个瓶子,此后第a[i]个瓶子不会再被用到.瓶子的容量可以视作是无限的. 吉丽知道某几对液体物质在一起时会发生反应产生沉淀,具体反应是1克c[i]物质和1克d[i]物质生成2克沉淀,一直进行直

BZOJ:3712: [PA2014]Fiolki

Description 化学家吉丽想要配置一种神奇的药水来拯救世界.吉丽有n种不同的液体物质,和n个药瓶(均从1到n编号).初始时,第i个瓶内装着g[i]克的第i种物质.吉丽需要执行一定的步骤来配置药水,第i个步骤是将第a[i]个瓶子内的所有液体倒入第b[i]个瓶子,此后第a[i]个瓶子不会再被用到.瓶子的容量可以视作是无限的.吉丽知道某几对液体物质在一起时会发生反应产生沉淀,具体反应是1克c[i]物质和1克d[i]物质生成2克沉淀,一直进行直到某一反应物耗尽.生成的沉淀不会和任何物质反应.当有

【BZOJ】3712: [PA2014]Fiolki

http://www.lydsy.com/JudgeOnline/problem.php?id=3712 题意:n个瓶子,第i个瓶子里又g[i]克物质.m次操作,第i次操作把第a[i]个瓶子的东西全部倒到第b[i]个瓶子里(保证之后不出现a[i]).k种反应,其中c[i]和d[i]反应,而且如果一个瓶子里有多种反应则优先反应靠前的.每次反应对答案贡献为min(g[i], g[i])*2(m<n<=200000, k<=500000) #include <bits/stdc++.h&

[bzoj3712][PA2014]Fiolki

description 题面 data range \[ 0\le m<n\le 200000,0\le k\le 500000\] solution 之前本人一直煞笔地思考暴力是否可行 考虑按照操作关系直接构树,之后按照每个反应中两点在树上的\(lca\)深度排序 最后依次考虑每个反应即可 虽然说建出来的也是个\(Kruskal\)重构树 code 没有按质合并的并查集都能过 #include<bits/stdc++.h> #include<algorithm> #incl

一些奇奇怪怪的过题思路

最近考了几次试,做完之后发现自己还是缺乏思维精度和深度--在此把一些奇怪的思路记下来-- 随 题意大概就是拿了一堆数取来取去,这些数在一个模数意义下做乘法,求出操作后取值的期望. 首先,找到这个模数的原根(鬼知道为什么现在出来了),然后就把这些乘法变成加法,然后就是矩阵一通乱搞-- 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using