【2020杭电多校第二场】Total Eclipse 思维+并查集

Total Eclipse

题意

给出一个 n 个点,m 条边的无向图,每个顶点都有一个价值\(b_i\),你可以执行以下操作:

选择一个连通块,处于这个连通块的所有顶点的价值减去 1 。

问最少需要多少次操作,使得所有的顶点价值全部变为0。

题解

参考博客:hdu6763 Total Eclipse 2020杭电多校第2场

我们每次从一个当前价值最小的顶点开始遍历,遍历到的顶点都减去该最小价值,如果某个顶点变为 0,那么就把这个点从图中抹去。直到所有顶点都被抹去。

这样复杂度太高,无法接受。

反着考虑:

image-20200724211713419

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\n",ans);
    }
    return 0;
}
/*
100
6 6
3 4 5 2 3 6
1 2
2 3
3 4
4 5
5 6
6 1
*/
posted @ 2020-07-24 21:23  Valk3  阅读(140)  评论(0编辑  收藏  举报