BZOJ3712: [PA2014]Fiolki

BZOJ3712: [PA2014]Fiolki

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

3 2 1
2 3 4
1 2
3 2
2 3

Sample Output

6

题解Here!
有思维难度的图论题。
先说一种错误的做法:

直接把反应的步骤连边,遇到可以反应的物质就判断这两种物质在不在一张图里。

这当然是极其标准的错误解法,因为这样完全没有考虑反应在时间上的先后顺序。

我当时就是这么想的,然后被我自己给$Hack$了$emmm......$

正解是这样的:

如果两个反应发生,要考虑两点:

  1. 反应发生的时间。
  2. 反应的优先程度。

并且要先考虑$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;
}
原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9508385.html