2020杭电多校 第二场 A 并查集+转换

在这里插入图片描述
题目让我们找出最小的操作次数 来完成要求 我们当然可以 从最小的点开始一直把这个图分裂下去,但是这样进行是特别慢的。
所以我们可以换一下思维,先把所有的点的权值加起来,我们知道这样的话是会有重复的,然后再把从小到大转换为从大到小的遍历,走到每一个节点时,找出与当前节点相连的点,如果相连的点之前已经被遍历过并且他们不再一个集合中,那么代表这个当前位置上的点的权值是重复的,把它减掉,然后再把他们放到一个集合中去(并查集完成即可)。这样完成遍历之后 ,剩下的权值和就是我们要的答案。

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int MAXN = 2e6+7;
vector<int>G[MAXN];
int n,m;
ll w[MAXN];
int pre[MAXN];
int vis[MAXN];
struct node
{
    
    
	ll w;
	int id;
}point[MAXN];

bool cmp(node a,node b){
    
    
	return a.w > b.w;
}

int Find(int x)
{
    
    
	if(pre[x] == x) return x;
	else return pre[x] = Find(pre[x]);
}

int main()
{
    
    
	int t;
	scanf("%d",&t);
	while(t--){
    
    
		ll ans = 0;
		scanf("%d%d",&n,&m);
		for(int i = 1;i <= n;i ++){
    
    
			pre[i] = i;
			vis[i] = 0;
			G[i].clear();
		}
		for(int i = 1;i <= n;i ++){
    
    
			scanf("%lld",&w[i]);
			point[i].w = w[i],point[i].id = i;
			ans += point[i].w;
		}
		while(m--){
    
    
			int u,v;
			scanf("%d%d",&u,&v);
			G[u].push_back(v);
			G[v].push_back(u);
		}
		//for(int i = 1;i <= n;i ++){
    
    
			//for(int j = 0;j < G[i].size();j ++) printf("---%d to is %d\n",i,G[i][j]);
		//}
		sort(point+1,point+1+n,cmp);
		//for(int i = 1;i <= n;i ++) printf("%d %d\n",point[i].id,point[i].w);
		for(int i = 1;i <= n;i ++){
    
    
			for(auto to : G[point[i].id]){
    
    
				if(vis[to]){
    
    
					int a = Find(point[i].id),b = Find(to);
					if(a != b){
    
    
						pre[b] = a;
						ans -= w[point[i].id];
					}
				}
				vis[point[i].id] = 1;
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45672411/article/details/108321841