题目想问的是每次取一个所能取到最大的连通块,并把他们的上面每个点的权值-1,最少需要多少次操作
这题顺着做,就是每次找到最大的连通块,然后-1,之后可能出现某些连接点权值为0断开,变成多个连通块继续做。
因此考虑倒着做,我们发现,每个点作为单独的连通块做出的贡献就是当旁边的点权值为0了,因此将点的权值排序后
从头枚举,对于每个点枚举他的邻边,如果邻点的权值大于他,就把他们两个集合合并,答案的贡献就是两个集合父节点的权值差
之后枚举最后父节点是自己的答案加上即可。

#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e5+10; const int mod=998244353; vector<int> g[N]; int b[N]; int p[N]; vector<int> now; int sign[N]; bool cmp(int x,int y){ return b[x]>b[y]; } int find(int x){ if(x!=p[x]){ p[x]=find(p[x]); } return p[x]; } int main(){ ios::sync_with_stdio(false); int t; cin>>t; while(t--){ int n,m; cin>>n>>m; int i; now.clear(); for(i=1;i<=n;i++){ cin>>b[i]; g[i].clear(); p[i]=i; now.push_back(i); sign[i]=0; } while(m--){ int x,y; cin>>x>>y; g[x].push_back(y); g[y].push_back(x); } sort(now.begin(),now.end(),cmp); ll ans=0; for(auto x:now){ for(i=0;i<g[x].size();i++){ int y=g[x][i]; if(b[y]<b[x]) continue; int pa=find(x); int pb=find(y); if(pa!=pb){ p[pb]=pa; ans+=b[pb]-b[pa]; } } } for(i=1;i<=n;i++) if(p[i]==i) ans+=b[i]; cout<<ans<<endl; } return 0; }