化学家吉丽想要配置一种神奇的药水来拯救世界。
吉丽有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]),按照反应的优先顺序给出。同一个反应不会重复出现。
直接把反应的步骤连边,遇到可以反应的物质就判断这两种物质在不在一张图里。
这当然是极其标准的错误解法,因为这样完全没有考虑反应在时间上的先后顺序。
我当时就是这么想的,然后被我自己给$Hack$了$emmm......$
正解是这样的:
如果两个反应发生,要考虑两点:
- 反应发生的时间。
- 反应的优先程度。
并且要先考虑$1$,再考虑$2$。
反应时间可以通过$LCA$解决:
每两个反应连在一个新的点上,这个点就代表了这两种物质,两种物质的$LCA$的深度就是它们反应时间的先后顺序。
煮个栗子:
比如共有$3$个瓶子,$2$个反应。
$1$和$2$先混合,那么就把$1,2$挂在$4$号节点的儿子上。
如果$3$再和$2$混合,那么就把$3$和$4$挂在$5$号节点的儿子上。
$1$与$3$的$LCA$是$5$,深度为$1$,相当于它们最后混合在一起。
最后$sort$一下,然后直接模拟反应过程即可。
附代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#define MAXN 400010
#define MAXM 500010
using namespace std;
int n,m,k,c=1,d=0,root;
int val[MAXN],pos[MAXN];
int head[MAXN],deep[MAXN],son[MAXN],size[MAXN],fa[MAXN],id[MAXN],top[MAXN];
struct Tree{
int next,to;
}a[MAXN<<1];
struct Question{
int x,y,deep,id;
}que[MAXM];
inline int read(){
int date=0,w=1;char c=0;
while(c<‘0‘||c>‘9‘){if(c==‘-‘)w=-1;c=getchar();}
while(c>=‘0‘&&c<=‘9‘){date=date*10+c-‘0‘;c=getchar();}
return date*w;
}
inline bool cmp(const Question &p,const Question &q){
if(p.deep==q.deep)return p.id<q.id;
return p.deep>q.deep;
}
inline void add(int x,int y){
a[c].to=y;a[c].next=head[x];head[x]=c++;
a[c].to=x;a[c].next=head[y];head[y]=c++;
}
inline void add_que(int x,int y,int lca,int id){
d++;
que[d].x=x;que[d].y=y;que[d].deep=deep[lca];
que[d].id=id;
}
void dfs1(int rt){
son[rt]=0;size[rt]=1;
for(int i=head[rt];i;i=a[i].next){
int will=a[i].to;
if(!deep[will]&&will!=fa[rt]){
deep[will]=deep[rt]+1;
fa[will]=rt;
id[will]=id[rt];
dfs1(will);
size[rt]+=size[will];
if(size[son[rt]]<size[will])son[rt]=will;
}
}
}
void dfs2(int rt,int f){
top[rt]=f;
if(son[rt])dfs2(son[rt],f);
for(int i=head[rt];i;i=a[i].next){
int will=a[i].to;
if(will!=fa[rt]&&will!=son[rt])dfs2(will,will);
}
}
int LCA(int x,int y){
while(top[x]!=top[y]){
if(deep[top[x]]<deep[top[y]])swap(x,y);
x=fa[top[x]];
}
if(deep[x]>deep[y])swap(x,y);
return x;
}
void work(){
int x,y;
long long ans=0;
sort(que+1,que+d+1,cmp);
for(int i=1;i<=d;i++){
x=que[i].x;y=que[i].y;
int w=min(val[x],val[y]);
val[x]-=w;val[y]-=w;
ans+=w;
}
printf("%lld\n",(ans<<1));
}
void init(){
int x,y;
n=read();m=read();k=read();
root=n;
for(int i=1;i<=n;i++){
val[i]=read();
pos[i]=i;
}
for(int i=1;i<=m;i++){
x=read();y=read();
root++;
add(root,pos[x]);add(root,pos[y]);
pos[y]=root;
}
for(int i=root;i>=1;i--)
if(!deep[i]){
deep[i]=1;id[i]=i;
dfs1(i);
dfs2(i,i);
}
for(int i=1;i<=k;i++){
x=read();y=read();
if(id[x]!=id[y])continue;
int lca=LCA(x,y);
add_que(x,y,lca,i);
}
}
int main(){
init();
work();
return 0;
}