1. 程式人生 > 實用技巧 >2020杭電HDU-6763多校第二場Total Eclipse(並查集)

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