zoukankan      html  css  js  c++  java
  • bzoj 4883 棋盘上的守卫 —— 基环树转化

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4883

    首先,注意到每个点可横可竖,但花费一样;

    所以考虑行列的交集,那么这个条件可以转化为行点和列点之间的边,边权就是花费;

    如果行和列都按原图交点连了边,那么问题就转化成在有向图中,每个点(原图的行/列)都有且仅有1入度;

    这个性质让我们联想到基环树,所以只需要构造一个基环树森林即可;

    又要边权和最小,仿照 Kruskal,从小到大加边,对于连通块,看看是否已经有环即可。

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    int const xn=2e5+5;
    int n,m,ct,fa[xn];
    ll ans;
    bool v[xn];
    struct N{int u,v,w;}ed[xn<<1];
    bool cmp(N x,N y){return x.w<y.w;}
    int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    int rd()
    {
        int ret=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();}
        while(ch>='0'&&ch<='9')ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar();
        return f?ret:-ret;
    }
    int main()
    {
        n=rd(); m=rd();
        for(int i=1;i<=n;i++)
            for(int j=1,z;j<=m;j++)
            {
                z=rd();
                ed[++ct].u=i; ed[ct].v=j+n; ed[ct].w=z;
            }
        for(int i=1;i<=n+m;i++)fa[i]=i;
        sort(ed+1,ed+ct+1,cmp);
        for(int i=1;i<=ct;i++)
        {
            int x=find(ed[i].u),y=find(ed[i].v),w=ed[i].w;
            if(x==y)
            {
                if(v[x])continue;
                else ans+=w,v[x]=1;
            }
            else
            {
                if(v[x]&&v[y])continue;
                else if(v[x]||v[y])ans+=w,fa[x]=y,v[y]=1;
                else ans+=w,fa[x]=y;
            }
        }
        printf("%lld
    ",ans);
        return 0;
    }
  • 相关阅读:
    angular11源码探索七[服务二]
    angular11源码探索六[服务基础一]
    有趣的特效,嘤嘤嘤
    angular11学习(十八)
    matplotlib 去掉小方框
    xlrd.biffh.XLRDError 问题报错
    页面点击出现蓝色背景色
    移动端不显示滚动条
    Swiper垂直方向滑动,高度获取不正确的解决方法
    二维树状数组
  • 原文地址:https://www.cnblogs.com/Zinn/p/9702852.html
Copyright © 2011-2022 走看看