【2020杭電多校第二場】Total Eclipse 思維+並查集
阿新 • • 發佈:2020-07-24
題意
給出一個 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 */