[USACO21OPEN] Portals G(Kruskal)
[USACO21OPEN] Portals G
解题思路
这个题目不是很复杂。我们可以先以每个状态为一个点建图,若两个状态可以相互到达,就可以连一条边。我们的任务就是让这张图联通。
容易发现,每个点的度都为 \(2\),结合题目,我们知道这张图一定是若干个不相交的环组成的。那么想要让这些换相交,我们就需要加一些边,也就是通过改变传送门顺序。
比如说现在有一个点 \(i\),\((i,p_2)\) 和 \((i,p_3)\) 这两个点不连通,我们就需要改变一下顺序,将 \((i,p_2),(i,p_3)\) 连起来,\((i,p_1),(i,p_4)\),那么两个环就连起来了。这个操作的花费是 \(c_i\) 块钱。
因此,我们很容易想到 Kruskal
,我们对所有 \(c_i\) 排序,然后查是否需要连这条边即可。
时间复杂度 \(O(n\log n)\)。
代码
//Don't act like a loser.
//This code is written by huayucaiji
//You can only use the code for studying or finding mistakes
//Or,you'll be punished by Sakyamuni!!!
#include<bits/stdc++.h>
using namespace std;
int read() {
char ch=getchar();
int f=1,x=0;
while(ch<'0'||ch>'9') {
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') {
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
const int MAXN=1e5+10;
int n;
int father[MAXN<<2],a[MAXN<<1][3],cnt[MAXN<<1];
int node(int i,int j) {
return (i-1)*4+j;
}
int find(int x) {
if(father[x]!=x) {
father[x]=find(father[x]);
}
return father[x];
}
struct coin {
int val,id;
}c[MAXN];
bool cmp(coin a,coin b) {
return a.val<b.val;
}
signed main() {
cin>>n;
for(int i=1;i<=n*4;i++) {
father[i]=i;
}
for(int i=1;i<=n;i++) {
c[i].val=read();c[i].id=i;
for(int j=1;j<=4;j++) {
int x=read();
a[x][++cnt[x]]=node(i,j);
}
for(int j=1;j<=3;j+=2) {
int u=node(i,j),v=node(i,j+1);
u=find(u);v=find(v);
father[u]=v;
}
}
for(int i=1;i<=2*n;i++) {
int u=a[i][2],v=a[i][1];
u=find(u);v=find(v);
father[u]=v;
}
sort(c+1,c+n+1,cmp);
int ans=0;
for(int i=1;i<=n;i++) {
int u=node(c[i].id,1),v=node(c[i].id,3);
u=find(u);v=find(v);
if(u!=v) {
ans+=c[i].val;
father[u]=v;
}
}
cout<<ans<<endl;
return 0;
}