1. 程式人生 > 實用技巧 >【2020杭電多校第二場】Total Eclipse 思維+並查集

【2020杭電多校第二場】Total Eclipse 思維+並查集

Total Eclipse

題意

給出一個 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\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
*/