D143 最小生成树 Kruskal 算法 P7528 [USACO21OPEN] Portals G
D143 最小生成树 Kruskal 算法 P7528 [USACO21OPEN] Portals G_哔哩哔哩_bilibili
P7528 [USACO21OPEN] Portals G - 洛谷
题意抽象,建图后发现,求多环连通的最小生成树。
思路
每个节点连 4 个传送门(即边),1、2 门连通,3、4 门连通,相当于每个节点有 2 条边穿过,样例建图如下
5 个节点,10 条边,构成了 3 个环,所以连通 3 个环的最小代价是选节点 4 和 1 的点权之和

所以,把环看做点,把节点的权看做新边权,求连通所有环的最小生成树,模仿 Kruskal 的思路
输入节点信息时,把边 $p_{i,1}$ 和 $p_{i,2}$ 并入集合,把边 $p_{i,3}$ 和 $p_{i,4}$ 并入集合
然后对节点按点权排序,相当于对新边排序
然后枚举节点,如果穿过节点的两条边没在一个集合,就并入一个集合,同时累加答案
相关板子:
D08【模板】最小生成树 Kruskal 算法 - 董晓 - 博客园
// 最小生成树 kruskal 算法 O(m*logm) #include<bits/stdc++.h> using namespace std; const int N=1e5+5; int n,ans; int c[N],p[N][5]; int fa[2*N]; pair<int,int> v[N]; int find(int x){ return fa[x]==x?x:fa[x]=find(fa[x]); } void merg(int x,int y){ fa[find(x)]=find(y); } int main(){ scanf("%d",&n); for(int i=1;i<=2*n;i++) fa[i]=i; //传送门编号 for(int i=1;i<=n;i++){ scanf("%d%d%d%d%d",&c[i],&p[i][1],&p[i][2],&p[i][3],&p[i][4]); merg(p[i][1],p[i][2]); merg(p[i][3],p[i][4]); //合并两个传送门 } for(int i=1;i<=n;i++) v[i]={c[i],i}; //绑定代价和点编号 sort(v+1,v+n+1); for(int i=1;i<=n;i++){ auto [c,j]=v[i]; int x=find(p[j][1]),y=find(p[j][3]); if(x!=y){ fa[x]=y; ans+=c; } } printf("%d",ans); }
浙公网安备 33010602011771号