Description
化学家吉丽想要配置一种神奇的药水来拯救世界。
吉丽有n种不同的液体物质,和n个药瓶(均从1到n编号)。初始时,第i个瓶内装着g[i]克的第i种物质。吉丽需要执行一定的步骤来配置药水,第i个步骤是将第a[i]个瓶子内的所有液体倒入第b[i]个瓶子,此后第a[i]个瓶子不会再被用到。瓶子的容量可以视作是无限的。
吉丽知道某几对液体物质在一起时会发生反应产生沉淀,具体反应是1克c[i]物质和1克d[i]物质生成2克沉淀,一直进行直到某一反应物耗尽。生成的沉淀不会和任何物质反应。当有多于一对可以发生反应的物质在一起时,吉丽知道它们的反应顺序。每次倾倒完后,吉丽会等到反应结束后再执行下一步骤。
吉丽想知道配置过程中总共产生多少沉淀。
Input
第一行三个整数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]),按照反应的优先顺序给出。同一个反应不会重复出现。
Output
Sample Input
2 3 4
1 2
3 2
2 3
Sample Output
题解Here!
直接把反应的步骤连边,遇到可以反应的物质就判断这两种物质在不在一张图里。
这当然是极其标准的错误解法,因为这样完全没有考虑反应在时间上的先后顺序。
我当时就是这么想的,然后被我自己给$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 ",(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; }