并查集重构树
[PA2014]Fiolki
bzoj3712
我们将每瓶药看成一个节点,对于一个操作合并x,y两瓶药就再新建一个节点代表这个操作,左右子节点分别是x,y两瓶药所在子树的根节点。
这样像kruskal重构树一样建出一棵二叉树,也叫并查集重构树。
可以发现对于每一对反应的两瓶药x,y,都是在它们在并查集重构树上的LCA处那个操作时发生反应的。
我们以每个反应的LCA深度为第一关键字,优先度(题目里给的)为第二关键字排序,然后模拟一下即可。
也可以并查集按秩合并
https://blog.csdn.net/The_OIer/article/details/100805617
细节写挂调了40min
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define re register
using namespace std;
typedef long long ll;
const int N=4e5+10;
inline int read() {
int x=0;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x;
}
int n,m,k;
int hd[N],to[N],nxt[N],tot;
inline void add(int x,int y) {
to[++tot]=y;nxt[tot]=hd[x];hd[x]=tot;
}
int f[N],siz[N],son[N],dep[N];
inline void dfs_son(int x,int fath) {
f[x]=fath;siz[x]=1;dep[x]=dep[fath]+1;
for(re int i=hd[x];i;i=nxt[i]) {
int y=to[i];
if(y==fath) continue;
dfs_son(y,x);
siz[x]+=siz[y];
if(siz[y]>siz[son[x]]) son[x]=y;
}
}
int top[N];
inline void dfs_chain(int x,int tp) {
top[x]=tp;
if(son[x]) dfs_chain(son[x],tp);
for(re int i=hd[x];i;i=nxt[i]) {
if(to[i]!=f[x]&&to[i]!=son[x])
dfs_chain(to[i],to[i]);
}
}
inline int LCA(int x,int y) {
while(top[x]!=top[y]) {
if(dep[top[x]]<dep[top[y]]) swap(x,y);
x=f[top[x]];
}
return dep[x]<dep[y]?x:y;
}
int g[N],fa[N];
inline int find(int x) {
return fa[x]==x?x:fa[x]=find(fa[x]);
}
struct Q{
int u,v,lca,id;
Q(){}
Q(int u_,int v_,int lca_,int id_):u(u_),v(v_),lca(lca_),id(id_){}
// bool operator < (const Q x) const {
// return dep[lca]!=dep[x.lca]?dep[lca]>dep[x.lca]:id<x.id;
// }
}q[500010];
inline bool cmp(Q a,Q b) {
return dep[a.lca]!=dep[b.lca]?dep[a.lca]>dep[b.lca]:a.id<b.id;
}
ll ans;
int main() {
n=read();m=read();k=read();
for(re int i=1;i<=n;i++) g[i]=read();
for(re int i=1;i<=n+m;i++) fa[i]=i;
int cnt=n;
for(re int i=1,a,b;i<=m;i++) {
a=read();b=read();
a=find(a),b=find(b);
fa[a]=fa[b]=++cnt;
add(cnt,a),add(cnt,b);
}
for(re int i=1;i<=n+m;i++)
if(find(i)==i)
dfs_son(i,0),dfs_chain(i,i);
for(re int i=1,a,b;i<=k;i++) {
a=read();b=read();
q[i]=Q(a,b,LCA(a,b),i);
}
sort(q+1,q+1+k,cmp);
for(re int i=1,a,b;i<=k;i++) {
a=q[i].u,b=q[i].v;
if(find(a)!=find(b)) continue;
int w=min(g[a],g[b]);
ans+=2*w;g[a]-=w;g[b]-=w;
}
printf("%lld
",ans);
return 0;
}