题目描述
农夫FJ的奶牛们有空旷恐惧症,所以,FJ打算在他的农场围上篱笆。他的农场是一个矩形区域。左上角的坐标是(0,0),右下角的坐标是(A,B),FJ修建了n(0<=n<=2000)个竖直的篱笆,其横坐标分别为a1,a2,a3,……,an,其中0<ai<A,,每一个篱笆从(ai,0)到(ai,B)也修建了m个水平的篱笆,其纵坐标为b1,b2,b3,……,bm,其中0<bi<B,每一个篱笆从(0,bi)到(A,bi)。这些篱笆把整个农场分成(n+1)*(m+1)个区域。
不幸的是FJ忘了在篱笆上装门了。这导致奶牛无法在不同的区域之间移动。于是他决定将某些篱笆拆掉。现在要使得所有的区域都联通,请问最少要拆掉多长的篱笆。
比如下面这个篱笆
+---+--+
| | |
+---+--+
| | |
| | |
+---+--+
可以这么拆:
+---+--+
| |
+---+ +
| |
| |
+---+--+
输入
第一题包含四个数A,B,n,m。(0<=A,B<=1000000000).
接下来有两行,第二行n个数,表示a1,a2,……,an,表示竖直的n个篱笆的横坐标,第三行m个数,表示b1,b2,b3,……,bm,表示m个水平的篱笆的纵坐标。
输出
最少要拆除的篱笆的总长度。结果可能超过int。请用long long int
样例输入
15 15 5 2 2 5 10 6 4 11 3
样例输出
44
解题思路
其实这道题仔细思考一下,或者画一下图,是可以很轻松地看出来,就是求一个最小生成树。
把每一个方形看做一个点,进行编号,那么肯定是有点可以互相连通的。边权就是他们篱笆的长度,求个最小生成树即可。但这道题数据量有点大,而最小生成树的算法却是还要排序,这样一弄时间就炸了。于是我们就需要进行离散化。
我们可以知道,边权相等的是有很多的,我们就把它们只算一下,这样进行排序,然后在计算。
但我这次要说一个比较快速的方法。其实思路跟最小生成树差不多。
我们依次删除最小长度的篱笆,处理好不多删,这样绝对是最优的,那该如何处理呢?我们先将横竖篱笆进行排序。首先,最小的横着的篱笆和竖着的篱笆是肯定都要建造的,这样是最优的。然后,就在横竖篱笆中选长度小的消除了。但做多次后,就会发现,有些点已经连通了,我们不用多删除这一篱笆,然而,这是否有规律呢?
如图,删除箭头所指的这一段篱笆时,有一些已经通过前面的删除可以连通的,因此我们就可以少删一点,此图可以少删1个篱笆。而删除的横着的篱笆那一段刚好为2。没错,就是这样的规律。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<vector>
#include<queue>
#include<cstdlib>
#include<algorithm>
#define N 2005
using namespace std;
int A,B,n,m , a[N] ,b[N];
long long ans;
int read(){
int f = 1 , x = 0;
char s = getchar ();
while (s < '0' || s > '9'){
if (s == '-')
f = -1;
s = getchar();
}
while (s >= '0' && s <= '9'){
x = x * 10 + s - '0';
s = getchar();
}
return x * f ;
}
int main(){
A = read();
B = read();
n = read();
m = read();
for (int i = 1;i <= n ;i ++)
a[i] = read();
for (int i = 1 ;i <= m ;i ++)
b[i] = read();
a[++n] = A;//需要加上最后一条边,那一段篱笆需要计算
b[++m] =B;
sort(a+1,a+1+n);
sort(b+1,b+1+m);
for (int i = n ; i >= 1 ;i --)//计算篱笆长度,这里分开
a[i] = a[i] - a[i - 1];
for (int i = m ; i >= 1 ;i --)
b[i] = b[i] - b[i - 1];
sort(a+1,a+1+n);
sort(b+1,b+1+m);
ans += 1ll * a[1] * (m - 1) + 1ll * b[1] * (n - 1);//最小的横竖篱笆一定要。
int cnta = 2 ,cntb = 2;
while (cnta <= n &&cntb <= m){//这里依次枚举
if (a[cnta] < b[cntb]){
ans += (1ll * a[cnta] * (m - cntb + 1));
cnta++;
}
else{
ans += (1ll * b[cntb] * (n - cnta + 1));
cntb ++;
}
}
printf("%lld",ans);
}