题解:
思路难想啊,OrzOrz。
这题主要就是想知道反应的先后顺序,我们可以对于每次倒药水,建一颗新的点,然后x向new连边,y向new连边。
这么一直建就变成了森林。
然后两个点反应就是两个点到他们的lca,我们可以根据lca的深度来判断反应的先后顺序,
然后直接模拟就好了。
代码:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=500010;
int n,m,k;
int g[N],f[N];
struct node{
int y,next;
}sa[N<<1];int len=1,first[N];
void ins(int x,int y)
{
len++;
sa[len].y=y;
sa[len].next=first[x];
first[x]=len;
}
void insert(int x,int y){ins(x,y);ins(y,x);}
int fa[N][24],t=0;
int dfn[N],dep[N];
void DFS(int now,int tim)
{
dfn[now]=tim;
for (int i=1; i<=20; i++)
if ((1<<i)<=dep[now]) fa[now][i]=fa[fa[now][i-1]][i-1];
else break;
for (int i=first[now]; i!=-1; i=sa[i].next)
if (sa[i].y!=fa[now][0])
{
dep[sa[i].y]=dep[now]+1;
fa[sa[i].y][0]=now;
DFS(sa[i].y,tim);
}
}
int LCA(int x,int y)
{
if (dep[x]<dep[y]) swap(x,y);
int dd=dep[x]-dep[y];
for (int i=0; (1<<i)<=dd; i++)
if (dd&(1<<i)) x=fa[x][i];
for (int i=20; i>=0; i--)
if (fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
if (x==y) return x;
return fa[x][0];
}
struct node1{
int x,y,lca,id;
}ss[N];int tot=0;
bool cmp(node1 x,node1 y)
{
if(x.lca==y.lca) return x.id<y.id;
return x.lca>y.lca;
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
memset(first,-1,sizeof(first));
for(int i=1;i<=n;i++)
scanf("%d",&g[i]);
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
insert(i+n,f[x]);
insert(i+n,f[y]);
f[y]=i+n;
}
memset(fa,0,sizeof(fa));
for(int i=n+m;i;i--) if(fa[i][0]==0) DFS(i,++t);
for(int i=1;i<=k;i++)
{
int x,y;
scanf("%d%d",&x,&y);
if(dfn[x]==dfn[y])
{
int yu=LCA(x,y);
ss[++tot].x=x;ss[tot].y=y;ss[tot].lca=dep[yu];ss[tot].id=i;
}
}
sort(ss+1,ss+1+tot,cmp);
long long ans=0;
for(int i=1;i<=tot;i++)
{
int yu=min(g[ss[i].x],g[ss[i].y]);
g[ss[i].x]-=yu;g[ss[i].y]-=yu;
ans+=(long long)yu;
}
printf("%lld\n",ans*2);
}