zoukankan      html  css  js  c++  java
  • noip2010关押罪犯

    https://www.zybuluo.com/ysner/note/1334611

    居然开始写水题题解了,noip退役预定

    题面

    戳我

    解析

    这道题似乎有三种做法:

    并查集

    我们知道,如果要求两个人不冲突,它们必须在不同监狱里。
    然而总共也只有两个监狱啊。
    所以对于一个人来说,要么和另一个人在同一监狱,要么和另一人不在同一监狱。
    这种二分图式的排斥关系,其实可以用并查集的补集来表示。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #define ll long long
    #define re register
    #define il inline
    #define fp(i,a,b) for(re int i=a;i<=b;++i)
    #define fq(i,a,b) for(re int i=a;i>=b;--i)
    using namespace std;
    const int N=1e5+100;
    int n,m,f[N];
    il int find(re int x){return x==f[x]?x:f[x]=find(f[x]);}
    struct dat{int u,v,w;il bool operator < (const dat &o) const {return w>o.w;}}a[N];
    il ll gi()
    {
      re ll x=0,t=1;
      re char ch=getchar();
      while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
      if(ch=='-') t=-1,ch=getchar();
      while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
      return x*t;
    }
    il void cmax(re int &x,re int y){x=(x>y?x:y);}
    int main()
    {
      n=gi();m=gi();
      fp(i,1,n*2) f[i]=i;
      fp(i,1,m)
        {
          re int u=gi(),v=gi(),w=gi();
          a[i]=(dat){u,v,w};
        }
      sort(a+1,a+1+m);
      fp(i,1,m)
        {
          re int u=a[i].u,v=a[i].v,w=a[i].w,fu=find(u),fv=find(v);
          if(fu==fv) return printf("%d
    ",w),0;
          f[fu]=find(v+n);f[fv]=find(u+n);
        }
      puts("0");
      return 0;
    }
    

    二分图染色

    仔细看看题,你会发现,要求的其实是最小化的最大值。
    这种问题可以二分。

    于是只连权值大于(mid)的边。
    然后判断所有边的两端是否能去不同的监狱。
    这个可以用二分图染色完成。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #define ll long long
    #define re register
    #define il inline
    #define fp(i,a,b) for(re int i=a;i<=b;++i)
    #define fq(i,a,b) for(re int i=a;i>=b;--i)
    using namespace std;
    const int N=1e5+100;
    int n,m,h[N],cnt,col[N],tag;
    struct Edge{int to,nxt;}e[N<<1];
    struct dat{int u,v,w;il bool operator < (const dat &o) const {return w>o.w;}}a[N];
    il void add(re int u,re int v){e[++cnt]=(Edge){v,h[u]};h[u]=cnt;}
    il ll gi()
    {
      re ll x=0,t=1;
      re char ch=getchar();
      while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
      if(ch=='-') t=-1,ch=getchar();
      while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
      return x*t;
    }
    il void dfs(re int u)
    {
      if(!tag) return;
      for(re int i=h[u];i+1;i=e[i].nxt)
        {
          re int v=e[i].to;
          if(col[v]==-1) col[v]=col[u]^1,dfs(v);
          else if(col[v]==col[u]) {tag=0;return;}
        }
    }
    il int check(re int x)
    {
      memset(h,-1,sizeof(h));cnt=0;memset(col,-1,sizeof(col));
      fp(i,1,m)
        if(a[i].w>x) add(a[i].u,a[i].v),add(a[i].v,a[i].u);
      tag=1;
      fp(i,1,n) if(col[i]==-1) col[i]=0,dfs(i);
      return tag;
    }
    il void cmax(re int &x,re int y){x=(x>y?x:y);}
    int main()
    {
      n=gi();m=gi();
      fp(i,1,m)
        {
          re int u=gi(),v=gi(),w=gi();
          a[i]=(dat){u,v,w};
        }
      sort(a+1,a+1+m);
      re int l=0,r=a[1].w,ans=0;
      while(l<=r)
        {
          re int mid=l+r>>1;
          if(check(mid)) ans=mid,r=mid-1;
          else l=mid+1;
        }
      printf("%d
    ",ans);
      return 0;
    }
    

    二分图

    众所周知,要形成二分图,图中不能有奇环。
    所以我们把边按边权排序后,只要一条边加入后形成了奇环,就可以输出答案了。

    所以怎么判呢?
    判是否形成环可以用并查集,判形成的环是否是奇环当然也可以用并查集。

    因为每次连边,我们都是把并差集的根结点相连。
    如果在之前根结点已经相连,再连边就会形成环。
    环的大小吗,就是两端点分别离根结点的距离之和(+1)。(当然不能路径压缩)

    所以每次合并时,我们维护一下每个点到其并查集父亲距离的奇偶性。
    求环大小时,从两端点分别暴跳父亲即可。

    #include<iostream>
    #include<cmath>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    #define re register
    #define il inline
    #define pc(a) putchar(a)
    #define fp(i,a,b) for(re int i=a;i<=b;i++)
    #define fq(i,a,b) for(re int i=a;i>=b;i--)
    using namespace std;
    const int N=2e5+100,inf=1e9+100;
    int f[N],n,m,dp[N],h[N];
    bool vis[N];
    ll ans;
    struct dat{int u,v,w;bool operator < (const dat &o) const {return w>o.w;}}a[N<<1];
    il int find(re int x){while(f[x]^x) x=f[x];return x;}
    il int Dis(re int x){re int res=0;while(f[x]^x) res^=dp[x],x=f[x];return res;}
    il int gi()
    {
      re int x=0,t=1;
      re char ch=getchar();
      while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
      if(ch=='-') t=-1,ch=getchar();
      while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
      return x*t;
    }
    int main()
    {
      n=gi();m=gi();
      fp(i,1,n) f[i]=i,h[i]=1;
      fp(i,1,m) a[i].u=gi(),a[i].v=gi(),a[i].w=gi();
      sort(a+1,a+1+m);
      fp(i,1,m)
        {
          re int u=a[i].u,v=a[i].v,fu=find(u),fv=find(v),w=a[i].w;
          if(fu^fv) dp[fu]^=Dis(u)^Dis(v)^1,f[fu]=fv;
          else if(Dis(u)^Dis(v)==0) {printf("%d
    ",w);return 0;}
        }
      puts("0");
      return 0;
    }
    
  • 相关阅读:
    字符的编码
    数据的基本类型和内置方法(二)
    基本的数据类型和内置方法介绍 (一)
    流程运算 if while for
    用户交换 基本数据类型 基本运算符 格式化输出
    机器语言发展简介和变量的介绍
    计算机基础
    Python学习建议和要求总结
    CH135 最大子序和 题解报告
    HRBUST1356 Leyni,罗莉和队列 题解报告
  • 原文地址:https://www.cnblogs.com/yanshannan/p/9920476.html
Copyright © 2011-2022 走看看