bzoj4410 [Usaco2016 Feb] Fence in

Description

有一个平面,左下角是(0,0),右上角是(A,B)。
有n个平行于y轴的栅栏a1..an,表示挡在(ai,0)到(ai,B)之间。
有m个平行于x轴的栅栏b1..bn,表示挡在(0,bi)到(A,bi)之间。
这样,平面被划成了(n+1)*(m+1)块。
现在要去掉某些栅栏的一部分,使得每一块都连通。
比如原来是这样:

可以去掉后变成这样:

求最少需要去掉多少长度的栅栏使得每一块都连通。

Input

第一行四个数A,B,n,m。
A<=1000000000 ,B<=1000000000 N<=25000 M<=25000
接下来n行每行一个数表示ai
接下来m行每行一个数表示bi。
0<ai<A  0<bi<B 

Output

输出一个数表示答案。
 
对边长排序后从小到大加边求最小生成树,一次加边加一行或一列,注意由于一部分可能已经连通,加边时要减去不需要加的边。
时间复杂度O(nlogn+mlogm+n+m)
#include<cstdio>
#include<algorithm>
int A,B,n,m;
int ns[25005],ms[25005];
int pn=0,pm=0;
long long ans=0;
inline int read(){
    int x=0,c=getchar();
    while(c>'9'||c<'0')c=getchar();
    while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();
    return x;
}
int main(){
    A=read();B=read();n=read();m=read();
    ns[0]=ms[0]=0;
    ns[n+1]=B;
    ms[m+1]=A;
    for(int i=1;i<=n;i++)ns[i]=read();
    for(int i=1;i<=m;i++)ms[i]=read();
    std::sort(ns,ns+n+2);
    std::sort(ms,ms+m+2);
    for(int i=0;i<=n;i++)ns[i]=ns[i+1]-ns[i];
    for(int i=0;i<=m;i++)ms[i]=ms[i+1]-ms[i];
    n++;m++;
    std::sort(ns,ns+n);
    std::sort(ms,ms+m);
    while(pn<n&&pm<m){
        if(ns[pn]<ms[pm])ans+=ns[pn]*1ll*(pm&&pn?m-pm:m-1),pn++;
        else ans+=ms[pm]*1ll*(pn&&pm?n-pn:n-1),pm++;
    }
    while(pn<n)ans+=ns[pn]*1ll*(pm&&pn?m-pm:m-1),pn++;
    while(pm<m)ans+=ms[pm]*1ll*(pn&&pm?n-pn:n-1),pm++;
    printf("%lld
",ans);
    return 0;
}

 

原文地址:https://www.cnblogs.com/ccz181078/p/5239834.html