2020杭电HDU-6763多校第二场Total Eclipse(并查集)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6763
CSDN食用链接:https://blog.csdn.net/qq_43906000/article/details/107605348

Problem Description

There are n cities and m bidirectional roads in Byteland. These cities are labeled by 1,2,…,n, the brightness of the i-th city is bi.

Magician Sunset wants to play a joke on Byteland by making a total eclipse such that the brightness of every city becomes zero. Sunset can do the following operations for arbitrary number of times:

· Select an integer k (1≤k≤n).

· Select k distinct cities \(c_1,c_2,…,c_k (1≤c_i≤n)\) such that they are connected with each other. In other words, for every pair of distinct selected cities ci and \(c_j\) (1≤i<j≤k), if you are at city ci, you can reach city \(c_j\) without visiting cities not in \({c_1,c_2,…,c_k}\).

· For every selected city \(c_i\) (1≤i≤k), decrease bci by 1.

Note that Sunset will always choose k with the maximum possible value. Now Sunset is wondering what is the minimum number of operations he needs to do, please write a program to help him.

Input
The first line of the input contains a single integer T (1≤T≤10), the number of test cases.

For each case, the first line of the input contains two integers n and m (1≤n≤100000, 1≤m≤200000), denoting the number of cities and the number of roads.

The second line of the input contains n integers\(b_1,b_2,…,b_n (1≤b_i≤10^9)\), denoting the brightness of each city.

Each of the following m lines contains two integers \(u_i\) and \(v_i(1≤u_i,v_i≤n,u_i≠v_i)\), denoting an bidirectional road between the \(u_i\)-th city and the \(v_i\)-th city. Note that there may be multiple roads between the same pair of cities.

Output
For each test case, output a single line containing an integer, the minimum number of operations.

Sample Input
1
3 2
3 2 3
1 2
2 3

Sample Output
4

题目大意:给你n个节点m条边的图,每个点有一个权值,你现在要做的操作是选择一个连通图,并将其中的每一个点的权值都减一,问你最少需要多少次才能将所有的点都变为0。

emmm,较为朴素的想法就是对每个连通图的点排序,然后将每个连通图中每个点都减去该连通图中最小的点的权值,然后就会图就会裂开,我们继续操作,直至所有的点都变成了0。
可以预见的是这种方法维护起来非常复杂,时空复杂度也非常高所以我们要换一种思路。

我们将小权值\(x\)往大权值\(y\)中合并,每一次的合并都会产生\(y-x\)的贡献,每次合并之后我们将大权值的父节点设置为小权值节点,最后我们再加上每个联通图的最小权值就好了。

大概思路就差不多是这样的,看代码会更容易理解些

以下是AC代码:

#include <bits/stdc++.h>
using namespace std;

const int mac=2e5+10;

struct Edge
{
	int to,next;
}eg[mac<<1];
int head[mac],num=0,father[mac],vis[mac];
int pa[mac];
struct Point
{
	int pos,val;
	bool operator <(const Point &a)const{
		return val>a.val;
	}
}pt[mac];

void add(int u,int v)
{
	eg[++num]=Edge{v,head[u]};
	head[u]=num;
}

int finds(int x){return x==father[x]?x:father[x]=finds(father[x]);}

void init()
{
	memset(head,-1,sizeof head);
	memset(vis,0,sizeof vis);
	num=0;
}

int main(int argc, char const *argv[])
{
	int t;
	scanf ("%d",&t);
	while (t--){
		init();
		int n,m;
		scanf ("%d%d",&n,&m);
		pa[0]=0;
		for (int i=1; i<=n; i++){
			int val;
			scanf ("%d",&val);
			pa[i]=val;father[i]=i;
			pt[i]=Point{i,val};
		}
		sort(pt+1,pt+1+n);
		for (int i=1; i<=m; i++){
			int u,v;
			scanf ("%d%d",&u,&v);
			add(u,v);add(v,u);
		}
		long long ans=0;
		for (int i=1; i<=n; i++){
			int u=pt[i].pos;
			vis[u]=1;
			for (int j=head[u]; j!=-1; j=eg[j].next){
				int v=eg[j].to;
				if (!vis[v]) continue;
				int rt=finds(v);
				if (rt==u) continue;
				ans+=pa[rt]-pa[u];
				father[rt]=u;
			}
		}
		for (int i=1; i<=n; i++)
			if (father[i]==i) ans+=pa[i];
		printf("%lld\n",ans);
	}
	return 0;
}
posted @ 2020-07-27 10:16  lonely_wind  阅读(199)  评论(0编辑  收藏  举报