题意:给出一个图,每个点有一个权值,破坏这个点需要花费这个点的权值,你的任务是破坏所有的点,若破坏某个点之前它的相邻点已经被破坏,那么它就不需要花费代价,求破坏所有点的最小代价
题解:
带权并查集
破坏一个点后,则这个点所在连通块的所有点就会被破坏,那么每个连通块都破坏其代价最小的点
若破坏的边不足k,则从小到大破坏其他的点
/* Attack */ #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<cmath> #define ll long long using namespace std; const int N = 1000010; int w[N],fa[N],q[N],v[N]; int gi() { int x=0,o=1; char ch=getchar(); while(ch!=‘-‘ && (ch<‘0‘ || ch>‘9‘)) ch=getchar(); if(ch==‘-‘) o=-1,ch=getchar(); while(ch>=‘0‘ && ch<=‘9‘) x=x*10+ch-‘0‘,ch=getchar(); return o*x; } int find(int x) { return x==fa[x]?x:fa[x]=find(fa[x]); } int main() { int T=gi(),tot=0; while(T--) { int n=gi(),m=gi(),k=gi(),cnt=0; for(int i=1; i<=n; i++) v[i]=w[i]=gi(),fa[i]=i; for(int i=1; i<=m; i++) { int x=gi(),y=gi(),xx,yy; xx=find(x),yy=find(y); if(xx==yy) {k--;continue;} if(w[xx]>w[yy]) swap(xx,yy); w[yy]=w[xx],fa[yy]=xx; } ll ans=0; for(int i=1; i<=n; i++) { if(i==fa[i]) ans+=w[i]; else q[++cnt]=v[i]; } sort(q+1,q+cnt+1); for(int i=1; i<=k; i++) ans+=q[i]; printf("Case #%d: %lld\n", ++tot,ans); } return 0; }
时间: 2024-10-05 12:19:49