USACO 2016 February Contest Gold T3: Fenced In

题目大意

有一个平面,左下角是(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,m25,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 } 

 

posted @ 2019-07-20 18:12  LI_dox  阅读(181)  评论(0编辑  收藏  举报