题目大意
有一个平面,左下角是(0,0),右上角是(A,B)。
有n个平行于y轴的栅栏a1..an,表示挡在(ai,0)到(ai,B)之间。有m个平行于x轴的栅栏b1..bn,表示挡在(0,bi)到(A,bi)之间。
这样,平面被划成了(n+1)*(m+1)块。现在要去掉某些栅栏的一部分,使得每一块都连通。
(0≤n,m≤25,000)
比如原来是这样:
+---+--+
| | |
+---+--+
| | |
| | |
+---+--+
可以去掉后变成这样:
+---+--+
| |
+---+ +
| |
| |
+---+--+
题目分析
看到“去掉某些栅栏的一部分,使得每一块都连通”,不难联想到最小生成树。然而观察数据范围, n,m最大可达2e5,这样分出来的块数可能多达 2e5*2e5 , 若是再排个序的话,复杂度肯定会爆炸,所以考虑优化。
由于每一条围栏都是平行于所对应的边框的,所以,同一列的水平围栏长度相同,同一行的竖直围栏也长度相等。
这样就可以把水平围栏和竖直围栏分开算了,贪心地直接删去整行或整列。
但是,在删的过程中,由于到后面,并不是一整行或一整列上所有的围栏都要删去,有的删去甚至会出现环路,所以......
用x数组记录水平围栏的长度,用y数组记录竖直围栏的长度。再用i,j两个指针记录当前扫到第几行或列。
当i,j都大于1的时候:
由于当x[i]<y[j]的时候,肯定要删第i列的水平栅栏,该列的栅栏个数为m(行数)-j(已经删了多少行)+1,对列有影响的是行(列与列不相交)
当x[i]>y[j]的时候,肯定要删第j行的竖直栅栏,该行的栅栏个数为n(列数)-i(已经删了多少列)+1,对行有影响的是列(行与行不相交)
//不太理解可以自己画一个图
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int MAXN=2e5+10; 4 typedef long long ll; 5 6 ll A,B,n,m,ans; 7 ll a[MAXN],b[MAXN]; 8 ll x[MAXN],y[MAXN]; 9 int main(){ 10 scanf("%lld%lld%lld%lld",&A,&B,&n,&m); 11 for(int i=1;i<=n;++i) 12 scanf("%lld",&a[i]); 13 for(int i=1;i<=m;++i) 14 scanf("%lld",&b[i]); 15 sort(a+1,a+n+1); 16 sort(b+1,b+m+1); 17 for(int i=1;i<=n;++i) 18 x[i]=a[i]-a[i-1]; 19 x[n+1]=A-a[n]; 20 for(int i=1;i<=m;++i) 21 y[i]=b[i]-b[i-1]; 22 y[m+1]=B-b[m]; 23 24 ++n;++m; 25 sort(x+1,x+n+1); 26 sort(y+1,y+m+1); 27 28 // for(int i=1;i<=n;++i) 29 // cout<<x[i]<<' '; 30 // for(int i=1;i<=m;++i) 31 // cout<<y[i]<<' '; 32 // puts(""); 33 ans=x[1]*(m-1)+y[1]*(n-1); 34 for(int i=2,j=2;i<=n&&j<=m;){ 35 if(x[i]<y[j]) 36 ans+=x[i++]*(m-j+1); 37 else 38 ans+=y[j++]*(n-i+1); 39 // cout<<ans<<endl; 40 } 41 printf("%lld\n",ans); 42 return 0; 43 }