[2020杭电多校第二场]1001 Total Eclipse(贪心+dsu)
考虑朴素思想,每次把一个最大连通图所有的点减去最小值,然后分裂成若干子图继续下去。但是又不好写又容易t, 那么考虑反着来如何在保证正确的情况通过加点的方式来找答案。
首先,最后删的一定都是原先最大的那些点,那么考虑从大到小排序。每次进来一个点,判断他的相邻点是否已经进来,如果有就合并(dsu)。每次答案增加的应该是,集合内的点数*相邻点权之差。
#pragma GCC optimize("-Ofast","-funroll-all-loops") //freopen("C://std/a.in","r",stdin); //freopen("C://std/b.txt","w",stdout); #include<bits/stdc++.h> #define ll long long #define PB push_back #define endl '\n' #define INF 0x3f3f3f3f #define LINF 0x3f3f3f3f3f3f3f3f #define ull unsigned long long #define lson rt << 1, l, mid #define rson rt << 1 | 1, mid + 1, r #define lowbit(x) (x & (-x)) #define rep(i, a, b) for(int i = a ; i <= b ; ++ i) #define per(i, a, b) for(int i = b ; i >= a ; -- i) #define clr(a, b) memset(a, b, sizeof(a)) #define in insert #define random(x) (rand()%x) #define PII(x, y) make_pair(x, y) #define fi first #define se second #define pi acos(-1) #define re register //std::ios::sync_with_stdio(false); using namespace std; const int maxn = 1e6 + 50; const int N = 1e5 + 10; const int M = N * 20; const ll mod = 1e9 + 9; int T, n, m; int a[maxn], vis[maxn], dad[maxn], q[maxn]; int find(int i){return i==dad[i]?i:dad[i]=find(dad[i]);} vector<int> g[maxn]; bool cmp(int x, int y){return a[x] > a[y];} signed main(){ int u, v; cin >> T; while(T --){ scanf("%d %d", &n, &m); rep(i, 1, n) scanf("%d", &a[i]), g[i].clear(), dad[i] = q[i] = i, vis[i] = 0; rep(i, 1, m){ scanf("%d %d", &u, &v); g[v].PB(u); g[u].PB(v); } sort(q + 1, q + n + 1, cmp); ll ans = 0, p = 0; q[n+1] = 0; rep(i, 1, n){ p ++; vis[q[i]] = 1; for(auto v : g[q[i]]){ if(!vis[v]) continue; int V = find(v); int U = find(q[i]); if(V != U) dad[U] = V, p --; } ans += p * (a[q[i]] - a[q[i+1]]); } cout << ans << endl; } return 0; }