[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(nlog 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;
}


原文地址:https://www.cnblogs.com/huayucaiji/p/USACO21OPENPortals.html