P5687 網格圖
阿新 • • 發佈:2020-09-16
演算法原理
根據 \(\operatorname{Kruskal}\) 演算法的運算規則,每次總是會把當前邊權最小,且連線著本不連通的兩個點的邊選中。
而在這道題目中,位於同一行或列的邊的邊權大小一定是相同的,因此一定會接連選完這一行或列上所有可行的邊。
思考過程
選擇一行/列後,整個被選中的行/列上所有的點都位於同一個連通分量。
故選完至少一行和至少一列後,接下來構成生成樹的過程中,一定保證只存在一個包含兩個及以上節點的連通分量。
這是一個關鍵點,思考過程可以以這個點分成兩部分。
-
在選完至少一行和一列之前,保證接下來選擇的任何行/列,這一行/列上所有的邊都會被選擇。
-
而在選完至少一行和一列之後,對於即將被選擇的一行來說,這一行上尚未被連線進最小生成樹的點的個數 \(=\)
因此總的演算法思路就明確了:
-
首先將所有的邊權值放到一起升序排列,注意記錄一下這個邊權是橫邊的邊權還是縱邊的邊權,開一個
pair
存很方便(自動按照第一關鍵字為索引進行升序排序)。一遍sort
進行排序。開兩個變數 \(l\text{(line)}\)、\(c\text{(column)}\) 儲存已經選擇的行/列數。 -
然後從小到大列舉排序後的邊權,在兩個統計變數都不為 \(0\)
-
在兩個統計變數都大於 \(0\) 之後,對於行來說,加入其包括的 \(m-c\) 條邊,即可保證這一行上所有的點全部加入最小生成樹,且不會影響到之後的選擇,故可以保證正確性。列同理。
Tips
- 排序時把所有的邊放在了一起,因此記得把陣列開成兩倍。
- 不開
long long
見祖宗
程式碼:
#include<bits/stdc++.h> #define LL long long using namespace std; const int Maxe = 3e5 + 5; pair<LL, bool> a[Maxe << 1]; int n, m; LL ans = 0; int main() { scanf("%d%d", &n, &m); for(register int i = 1; i <= n; ++i) { int x; scanf("%d", &x); a[i] = make_pair(x, false); } for(register int i = n + 1; i <= n + m; ++i) { int x; scanf("%d", &x); a[i] = make_pair(x, true); } sort(a + 1, a + n + m + 1); int l = 0, c = 0; for(register int i = 1; i <= n + m; ++i) { if(!a[i].second) { if((!l)||(!c)) { ans += (long long)(m - 1) * a[i].first; } else { ans += (long long)(m - c) * a[i].first; } l++; } else { if((!l)||(!c)) { ans += (long long)(n - 1) * a[i].first; } else { ans += (long long)(n - l) * a[i].first; } c++; } } printf("%lld", ans); return 0; }