[PA2014] Fiolki

丶灬走出姿态 提交于 2019-12-03 23:29:55

问题描述

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

吉丽有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

解析

初步想法是把每个操作的两个容器连一条边\(a->b\),那么所有的操作构成了一棵树。可以知道每个反应发生的地方一定在两个反应物的LCA上。但是,如果两个反应的LCA相同,会出现很多情况,我们很难确定这两个反应的顺序。所以,考虑改变图模型的构造方法。

反应发生的顺序由反应先后和操作的先后共同决定。我们可以把两个操作的点连向一个新的点,以后如果还需要使用这两个药瓶中的一个,就用这个新点代替。这样,用反应的LCA的深度和先后就可以确定反应顺序。然后模拟即可。

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#define int long long
#define N 500002
#define M 1000002
using namespace std;
struct event{
    int a,b,lca,id;
}c[M];
int head[N],ver[N*2],nxt[N*2],l;
int n,m,k,i,cnt,g[N],d[N],dep[N],fa[N],son[N],size[N],top[N],f[N];
int read()
{
    char c=getchar();
    int w=0;
    while(c<'0'||c>'9') c=getchar();
    while(c<='9'&&c>='0'){
        w=w*10+c-'0';
        c=getchar();
    }
    return w;
}
void insert(int x,int y)
{
    l++;
    ver[l]=y;
    nxt[l]=head[x];
    head[x]=l;
}
void dfs1(int x,int pre)
{
    fa[x]=pre;
    dep[x]=dep[pre]+1;
    size[x]=1;
    for(int i=head[x];i;i=nxt[i]){
        int y=ver[i];
        dfs1(y,x);
        size[x]+=size[y];
        if(size[y]>size[son[x]]) son[x]=y;
    }
}
void dfs2(int x,int t)
{
    top[x]=t;
    if(son[x]) dfs2(son[x],t);
    for(int i=head[x];i;i=nxt[i]){
        int y=ver[i];
        if(y!=fa[x]&&y!=son[x]) dfs2(y,y);
    }
}
int LCA(int u,int v)
{
    while(top[u]!=top[v]){
        if(dep[top[v]]>dep[top[u]]) swap(u,v);
        u=fa[top[u]];
    }
    if(dep[u]>dep[v]) swap(u,v);
    return u;
}
int my_comp(const event &x,const event &y)
{
    if(dep[x.lca]!=dep[y.lca]) return dep[x.lca]>dep[y.lca];
    return x.id<y.id;
}
int find(int x)
{
    if(x!=f[x]) f[x]=find(f[x]);
    return f[x];
}
signed main()
{
    n=read();m=read();k=read();
    for(i=1;i<=n+m;i++) f[i]=i;
    for(i=1;i<=n;i++) g[i]=read();
    for(i=1;i<=m;i++){
        int a=read(),b=read(),f1=find(a),f2=find(b);
        n++;d[f1]++;d[f2]++;
        insert(n,f1);
        insert(n,f2);
        f[f1]=f[f2]=n;
    }
    for(i=1;i<=n;i++){
        if(d[i]==0) dfs1(i,0),dfs2(i,i);
    }
    for(i=1;i<=k;i++){
        int x=read(),y=read(),z=LCA(x,y);
        if(z==0) continue;
        cnt++;
        c[cnt].a=x,c[cnt].b=y;
        c[cnt].lca=z,c[cnt].id=i;
    }
    sort(c+1,c+cnt+1,my_comp);
    int ans=0;
    for(i=1;i<=cnt;i++){
        int tmp=min(g[c[i].a],g[c[i].b]);
        g[c[i].a]-=tmp;g[c[i].b]-=tmp;
        ans+=tmp*2;
    }
    printf("%lld\n",ans);
    return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!