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);
}

 

posted @ 2026-04-20 16:05  董晓  阅读(84)  评论(0)    收藏  举报