问题描述
化学家吉丽想要配置一种神奇的药水来拯救世界。
吉丽有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;
}