题意
给出一个 n 个点,m 条边的无向图,每个顶点都有一个价值(b_i),你可以执行以下操作:
选择一个连通块,处于这个连通块的所有顶点的价值减去 1 。
问最少需要多少次操作,使得所有的顶点价值全部变为0。
题解
参考博客:hdu6763 Total Eclipse 2020杭电多校第2场
我们每次从一个当前价值最小的顶点开始遍历,遍历到的顶点都减去该最小价值,如果某个顶点变为 0,那么就把这个点从图中抹去。直到所有顶点都被抹去。
这样复杂度太高,无法接受。
反着考虑:
d 就是连通块的个数。
代码
/*
* @Autor: valk
* @Date: 2020-07-17 16:50:40
* @LastEditTime: 2020-07-24 20:44:04
* @Description: 你背叛了工人阶级,操 你 妈!
*/
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int N = 2e5 + 10;
int fa[N],b[N],arr[N],vis[N],n,m;
vector<int>vec[N],valk;
bool cmp(int x,int y){
return b[x]>b[y];
}
set<int>s;
int find(int x){
if(x==fa[x]){
return x;
}
return fa[x]=find(fa[x]);
}
int main(){
int T;
scanf("%d",&T);
while(T--){
memset(vis,0,sizeof(vis));
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
vec[i].clear();
scanf("%d",&b[i]);
fa[i]=arr[i]=i;
}
for(int i=1,u,v;i<=m;i++){
scanf("%d%d",&u,&v);
vec[u].pb(v),vec[v].pb(u);
}
sort(arr+1,arr+1+n,cmp);
ll ans=0;
for(int i=1;i<=n;i++){
vis[arr[i]]=1;
s.clear(),valk.clear();
int uu=find(arr[i]);
for(int v:vec[arr[i]]){
if(vis[v]==0) continue;
int vv=find(v);
valk.pb(vv);
s.insert(vv);
}
for(int v:valk){
fa[v]=uu;
}
ans-=1LL*(s.size()-1)*b[arr[i]];
}
printf("%lld
",ans);
}
return 0;
}
/*
100
6 6
3 4 5 2 3 6
1 2
2 3
3 4
4 5
5 6
6 1
*/