bzoj3430 [Usaco2014 Jan]Ski Course Rating 并查集+bfs

来源:互联网 发布:crystalball for mac 编辑:程序博客网 时间:2024/06/16 20:39

这题正解脑洞有点大。。一开始并没有想到正解,只是会二分暴力= =
后来膜了一发题解发现原来还有人二分+可持久化并查集做的,不过这是针对t不同的情况下= =以后可以出个题祸害社会
正解是先把任意两点之间连边,记录一下出发点和到达点,然后按照边的大小排序,从小到大做,每一次用并查集将可以互通的两块联合在一起,直接计算贡献。。

#include<cstdio>#include<algorithm>#include<cstring>#include<iostream>#define fo(i,a,b) for(int i=a;i<=b;i++)#define fd(i,a,b) for(int i=a;i>=b;i--)using namespace std;typedef long long ll;const int inf=1e9;int n,m;const int dx[2]={0,1};const int dy[2]={1,0};const int N=600*600;int ez[N],ey[N],ex[N];int f[N],siz[N],cnt[N],eid[N];int t,h[600][600],id[600][600],tot;bool cmp(int a,int b){    return ez[a]<ez[b];}inline int find(int x){    return x==f[x]?x:f[x]=find(f[x]);}int main(){    scanf("%d%d%d",&n,&m,&t);    fo(i,1,n)    {        fo(j,1,m)        {            scanf("%d",&h[i][j]);            id[i][j]=++tot;            f[tot]=tot;            siz[tot]=1;        }    }    tot=0;    fo(i,1,n)    {        fo(j,1,m)        {            int x;            scanf("%d",&x);            if (x)cnt[id[i][j]]=1;            fo(k,0,1)            {                int x=i+dx[k];                int y=j+dy[k];                if (x>=1&&x<=n&&y>=1&&y<=m)                {                    ex[++tot]=id[i][j];                    ey[tot]=id[x][y];                    ez[tot]=abs(h[i][j]-h[x][y]);                    eid[tot]=tot;                }            }        }    }    sort(eid+1,eid+1+tot,cmp);    ll ans=0;    fo(i,1,tot)    {        int x=eid[i];        int a=find(ex[x]),b=find(ey[x]);        if (a==b)continue;        if (siz[a]+siz[b]>=t)        {            if (siz[a]<t)ans+=1ll*cnt[a]*ez[x];            if (siz[b]<t)ans+=1ll*cnt[b]*ez[x];            //注意这里直接把ez[x]当作边是因为已经从小到大排序了,所以能保证ez[x]是两个块中最大的边。        }        if (siz[a]<siz[b])swap(a,b);        f[b]=a;        siz[a]+=siz[b];        cnt[a]+=cnt[b];    }    printf("%lld\n",ans);} 
原创粉丝点击