• BZOJ4025 二分图


    4025: 二分图

    Time Limit: 20 Sec  Memory Limit: 512 MB

    Description

    神犇有一个n个节点的图。因为神犇是神犇,所以在T时间内一些边会出现后消失。神犇要求出每一时间段内这个图是否是二分图。这么简单的问题神犇当然会做了,于是他想考考你。

    Input

    输入数据的第一行是三个整数n,m,T。
    第2行到第m+1行,每行4个整数u,v,start,end。第i+1行的四个整数表示第i条边连接u,v两个点,这条边在start时刻出现,在第end时刻消失。

    Output

    输出包含T行。在第i行中,如果第i时间段内这个图是二分图,那么输出“Yes”,否则输出“No”,不含引号。

    Sample Input

    3 3 3
    1 2 0 2
    2 3 0 3
    1 3 1 2

    Sample Output

    Yes
    No
    Yes

    HINT

    样例说明:
    0时刻,出现两条边1-2和2-3。
    第1时间段内,这个图是二分图,输出Yes。
    1时刻,出现一条边1-3。
    第2时间段内,这个图不是二分图,输出No。
    2时刻,1-2和1-3两条边消失。
    第3时间段内,只有一条边2-3,这个图是二分图,输出Yes。
    数据范围:
    n<=100000,m<=200000,T<=100000,1<=u,v<=n,0<=start<=end<=T。
     
    因为这题太神我就放上来了。
    因为我也是看的题解,所以就写一下理理思路。
    首先我想到了二分图没有奇环这个东西,于是我满脑子的关押罪犯。
    然后我又想到了动态树,但是并没有想到怎么维护什么东西。
    想了很久无果,于是打开了PoPoQQQ。
    最后得出结论:这个东西我应该想不到。
     
    首先有一个结论:连边后形成树结构,如果新加边不构成奇环,则没必要加边。
    因为如果后面有边加入与这条边构成奇环,则与树上也一定构成奇环。
    因为树是二分图,想想就好了,感性接受还是挺容易的。
    然后,因为没有删边操作,所以只要加上一条边出现奇环,整个时间段就一定无解。
    知道了上面那两个结论就可以做题了。
    所以上面那个东西就维护以删除时间为边权的最大生成树就好了。
     
    然后这题竟然可以分治?而且分治的思想/方式也很巧妙。
    按时间进行类似线段树式分治(就是按mid分治)。
    每一次先把完全覆盖了当前区间的边加进并查集,并判断有无奇环。若有奇环,则整个区间都无解,直接return。
    没有奇环,则把边分成两边(跨过中间的拆开),递归处理。
    至于怎么判断奇环?这就是一个比较神奇的东西:我们可以用带权并查集。
    有个很经典的东西:判断相加是不是奇数,异或起来看最后一位就可以了。
    于是维护一个带权并查集,dis记录的是i到并查集代表元的距离,一路跳一路异或就可以了。
    其实好像相加也没什么吧?
    带权并查集就是很正常的操作了。
    因为分治是递归处理的,所以我们没必要每次加很多边,在上一层的基础上把上一层新找的边加进来就可以了。
    没必要写可持久化并查集(把数组可持久化?),只要写一个支持撤销的带权并查集,方便回溯后快速回到原本状态。
    这个东西用几个栈搞搞,不写路径压缩就可以了。
    因为不能写路径压缩,必须写按秩合并来保证复杂度。
    我分别用以最大深度为秩和以节点个数为秩的交了一发,发现以最大深度为秩的快一些。
    因为线段树的那一套理论,一个线段顶多被拆成O(logn)个区间,所以复杂度是O(nlog_2n)的。
    我是用的vector下传边所以比较慢,想快一点的可以改成数组。  
    #include    <iostream>
    #include    <cstdio>
    #include    <cstdlib>
    #include    <algorithm>
    #include    <vector>
    #include    <cstring>
    #include    <queue>
    #include    <complex>
    #include    <stack>
    #define LL long long int
    #define dob double
    #define FILE ""
    using namespace std;
    
    const int N = 100010;
    struct edge{int x,y,st,ed;};
    struct U_F_set{
      int fa[N],dis[N],size[N],stack1[N],stack2[N],stack3[N],top;
      inline int find(register int x){
        while(fa[x]!=x)x=fa[x];
        return x;
      }
      inline int getdis(register int x,register int ans=0){
        while(x!=fa[x])
          ans^=dis[x],x=fa[x];
        return ans;
      }
      inline void merge(register int x,register int y,register int val){
        if(size[x]>size[y]){
          top++;stack1[top]=y;stack2[top]=x;stack3[top]=size[x];
          fa[y]=x;dis[y]=val;size[x]+=size[y];
        }
        else{
          top++;stack1[top]=x;stack2[top]=y;stack3[top]=size[y];
          fa[x]=y;dis[x]=val;size[y]+=size[x];
        }
      }
      inline void rewrite(){
        size[stack2[top]]=stack3[top];fa[stack1[top]]=stack1[top];
        dis[stack1[top]]=0;top--;
      }
    }UF;
    int n,m,T,Ans[N];
    
    inline int gi(){
      int x=0,res=1;char ch=getchar();
      while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
      while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
      return x*res;
    }
    
    inline void solve(int l,int r,vector<edge>Edge){
      int mid=(l+r)>>1,now=UF.top;
      vector<edge>El,Er;
      for(int i=0,I=Edge.size();i<I;++i){
        edge e=Edge[i];
        if(e.st==l && e.ed==r){
          int x=e.x,y=e.y;
          int f1=UF.find(x),f2=UF.find(y);
          int dis=UF.getdis(x)^UF.getdis(y)^1;
          if(f1!=f2)UF.merge(f1,f2,dis);
          else if(dis&1){
            for(int i=l;i<=r;++i)Ans[i]=1;
            while(now!=UF.top)UF.rewrite();
            return;
          }
        }
        else if(e.ed<=mid)El.push_back(e);
        else if(e.st>mid)Er.push_back(e);
        else{
          edge el=e,er=e;el.ed=mid;er.st=mid+1;
          El.push_back(el);Er.push_back(er);
        }
      }
      if(l==r)Ans[l]=0;else solve(l,mid,El),solve(mid+1,r,Er);
      while(now!=UF.top)UF.rewrite();
    }
    
    int main()
    {
      n=gi();m=gi();T=gi();
      vector<edge>Edge;
      for(int i=1;i<=n;++i)UF.fa[i]=i,UF.size[i]=1;
      for(int i=1;i<=m;++i){
        int u=gi(),v=gi(),st=gi()+1,ed=gi();
        if(st>ed)continue;
        Edge.push_back((edge){u,v,st,ed});
      }
      solve(1,T,Edge);
      for(int i=1;i<=T;++i)
        printf("%s
    ",Ans[i]?"No":"Yes");
      return 0;
    }
    二分图
  • 相关阅读:
    Membership和Role Providers
    浏览器兼容手册
    手机开发与测试的Firefox插件:User Agent Switcher
    控制input输入框的高度
    纯 CSS3 打造的按钮实例
    CSS对各个浏览器兼容
    网页配色的天然范儿
    Jquery的each里面用return false代替break; return ture 代替continue
    li标签float:left,IE6中第二行会空缺一块,ie8和FF正常,怎么解决?
    用XMLHTTP实现无刷新的与server通信
  • 原文地址:https://www.cnblogs.com/fenghaoran/p/7608175.html
Copyright © 2020-2023  润新知