题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6763
博客园食用链接:https://www.cnblogs.com/lonely-wind-/p/13383968.html
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 ) c_1,c_2,…,c_k (1≤c_i≤n) c1,c2,…,ck(1≤ci≤n) such that they are connected with each other. In other words, for every pair of distinct selected cities ci and c j c_j cj (1≤i<j≤k), if you are at city ci, you can reach city c j c_j cj without visiting cities not in c 1 , c 2 , … , c k {c_1,c_2,…,c_k} c1,c2,…,ck.
· For every selected city c i c_i ci (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 ≤ 1 0 9 ) b_1,b_2,…,b_n (1≤b_i≤10^9) b1,b2,…,bn(1≤bi≤109), denoting the brightness of each city.
Each of the following m lines contains two integers u i u_i ui and v i ( 1 ≤ u i , v i ≤ n , u i ≠ v i ) v_i(1≤u_i,v_i≤n,u_i≠v_i) vi(1≤ui,vi≤n,ui=vi), denoting an bidirectional road between the u i u_i ui-th city and the v i v_i vi-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 x x往大权值 y y y中合并,每一次的合并都会产生 y − x y-x 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;
}