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