【最小生成樹】【並查集】[USACO2016 金組]Fenced In
題目描述
Farmer John has realized that many of his cows are strangely agoraphobic (being fearful of large open spaces). To try and make them less afraid of grazing, he partitions his large field into a number of smaller regions by building vertical (north-south) and horizontal (east-west) fences.
The large field is a rectangle with corner points at (0,0) and (A,B). FJ builds n vertical fences (0≤n≤2000) at distinct locations a1…ana1…an (0<
INPUT FORMAT (file fencedin.in):
The first line of input contains AA, BB, nn, and mm (1≤A,B≤1,000,000,000). The next nn lines contain
OUTPUT FORMAT (file fencedin.out):
Please write the minimum length of fencing FJ must remove. Note that this might be too large to fit into a standard 32-bit integer, so you may need to use 64-bit integer types (e.g., “long long” in C/C++).
SAMPLE INPUT:
15 15 5 2
2
5
10
6
4
11
3
SAMPLE OUTPUT:
44
題目分析
很顯然給每個方格編號相鄰的連線邊的權值為邊的長度,但是因為邊的數量太大,我們不能直接儲存,我們發現每一行的所有邊的長度都相同,同理每一列的長度也相同,所以我們有隻用儲存每一行的長度加邊的時候在遍歷該長度所在的行或者列進行連邊,然後同時我們用啟發式合併的方法進行合併並查集能夠勝任本題目。
程式碼
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
typedef long long ll;
const int MAXN = 2001 * 2001;
int Fa[MAXN+10], n, m, Rank[MAXN+10];
int A, B;
int As[2010], Bs[2010];
typedef pair<int, pair<int, int> > plii;
typedef pair<int, int> pii;
plii lis[2010*2+10];
char ch;
void Read(int &s){
while((ch=getchar()) < '0' || ch > '9');
s = ch - '0';
while((ch=getchar()), ch>='0'&&ch<='9')
s = s*10+ch-'0';
ungetc(ch, stdin);
}
int findFa(int u){
if(!Fa[u]) return u;
return Fa[u]=findFa(Fa[u]);
}
inline int GetID(int x, int y){return (n+1)*x + y;}
int main(){
//printf("%.3lf\n", 1.0*(sizeof(lis) +sizeof(Fa))/1024/1024);
Read(A); Read(B); Read(n); Read(m);
for(int i=1;i<=n;++i) Read(As[i]);
sort(As+1, As+1+n);
for(int i=1;i<=m;++i) Read(Bs[i]);
sort(Bs+1, Bs+1+m);
Bs[m+1] = B; As[n+1] = A;
int cnt = 0;
for(int i=0;i<=m;++i) lis[cnt++] = plii(Bs[i+1]-Bs[i],pii(-1, i));
for(int i=0;i<=n;++i) lis[cnt++] = plii(As[i+1]-As[i],pii(i, -1));
long long ans = 0, did = 0, Max = (n+1)*(m+1)-1;
sort(lis, lis+cnt);
for(int i=0;i<cnt && did<Max;++i){
if(lis[i].second.first == -1){
for(int j=0;j<n;++j){
int p1=GetID(lis[i].second.second, j);
int p2=GetID(lis[i].second.second, j+1);
int f1=findFa(p1), f2=findFa(p2);
if(f1 == f2) continue;
if(Rank[f1] > Rank[f2]) swap(f1, f2);
Fa[f1] = f2;
Rank[f2] = max(Rank[f1]+1, Rank[f2]);
ans += 1LL * lis[i].first;
++did;
}
}else{
for(int j=0;j<m;++j){
int p1=GetID(j, lis[i].second.first);
int p2=GetID(j+1, lis[i].second.first);
int f1=findFa(p1), f2=findFa(p2);
if(f1 == f2) continue;
if(Rank[f1] > Rank[f2]) swap(f1, f2);
Fa[f1] = f2;
Rank[f2] = max(Rank[f1]+1, Rank[f2]);
ans += 1LL * lis[i].first;
++did;
}
}
}
printf("%lld\n", ans);
return 0;
}