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