【最小生成树】[USACO 2016 February Contest, Gold]Fenced In

题目大意

农夫FJ的奶牛们有空旷恐惧症,所以,FJ打算在他的农场围上篱笆。他的农场是一个矩形区域。左上角的坐标是(0,0),右下角的坐标是(A,B),FJ修建了n(0<=n<=2000)个竖直的篱笆,其横坐标分别为a1,a2,a3,……,an,其中0

分析

可以轻易地看出这道题是要求最小生成树,处理出连通块之间栅栏的长度然后做最小生成树即可。
但是这样会Time Limited Exceed,尽管算法时间复杂度为O(n2log2n)
我们进行一些优化:由于每一行或每一列中的相邻两个连通块之间的栅栏长度相等,我们可以考虑使用kruskal算法,仅仅对每两个栅栏之间的距离排序,然后然后再对这一行(列)的所有相邻连通块连边即可。
算法时间复杂度还是O(n2log2n)
还要注意常数优化。

代码

#include<cstdio>
#include<algorithm>
#define MAXN 2000
using namespace std;
long long ans;
int n,a[MAXN+10],b[MAXN+10],A,B,m,tot,fa[(MAXN+1)*(MAXN+1)+10];
void Read(int &x){
    char c;
    while(c=getchar(),c!=EOF)
        if(c>='0'&&c<='9'){
            x=c-'0';
            while(c=getchar(),c>='0'&&c<='9')
                x=x*10+c-'0';
            ungetc(c,stdin);
            return;
        }
}
void read(){
    int i,t;
    Read(A),Read(B),Read(n),Read(m);
    for(i=1;i<=n;i++)
        Read(a[i]);
    for(i=1;i<=m;i++)
        Read(b[i]);
    sort(a+1,a+n+1);
    sort(b+1,b+m+1);
    a[++n]=A;
    b[++m]=B;
    for(i=n;i;i--)
        a[i]-=a[i-1];
    for(i=m;i;i--)
        b[i]-=b[i-1];
    sort(a+1,a+n+1);
    sort(b+1,b+m+1);
    t=n*m;
    for(i=1;i<=t;i++)
        fa[i]=i;
}
int find(int x){
    return x==fa[x]?x:fa[x]=find(fa[x]);
}
void kruskal(){
    int i,j,k,l;
    i=j=1;
    while(i<=n||j<=m){
        if(j>m||(i<=n&&a[i]<=b[j])){
            k=(i-1)*m+1;
            for(l=1;l<m;l++,k++)
                if(find(k)!=find(k+1))
                    fa[fa[k]]=fa[k+1],ans+=a[i];
            ++i;
        }
        else{
            k=j;
            for(l=1;l<n;l++,k+=m)
                if(find(k)!=find(k+m))
                    fa[fa[k]]=fa[k+m],ans+=b[j];
            ++j;
        }
    }
}
int main()
{
    read();
    kruskal();
    printf("%lld\n",ans);
}
posted @ 2016-03-12 16:03  outer_form  阅读(170)  评论(0编辑  收藏  举报