码迷,mamicode.com
首页 > 其他好文 > 详细

BZOJ 4025: 二分图

时间:2017-10-01 23:46:06      阅读:297      评论:0      收藏:0      [点我收藏+]

标签:names   递归   代码   表示   desc   证明   --   自己   return   

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。
 
题解:
  这个题目,我们考虑套路CDQ 图分治,以边出现的时间L,R分治,每次我们算区间(L~R)十分可能。
  对于时间段L~R,我们先把时间等于L~R的边加入图中,如果出现了基环,那么这一段时间都是不可能的,否则,我们将剩下的边按照<mid,>mid和在中间分类,把在中间的边拆成两个进行下一次递归,如果递归到最后一层都没有问题就是好的。
  然后,我们用并查集维护这个图的联通性,带一个权,维护这个点到到父亲的边数的基偶性,这样就可以维护出图中十分存在基环,具体,如果在一个并查集中,那么环的基偶性就是get(x)^get(y)^1,get(x)是x到首元素的基偶性,因为异或就相当于加,所以画一个图就理解了。对于合并,就直接讲首按照dep合并,这样是logn的,如果fa[fx]=fy,就改一下fx的权,改get(x)^get(y)^1,get(x),这个是等效的,证明的话自己思考一下,还是比较有趣的。
 
代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#include <vector>
#define MAXN 201000
using namespace std;
int fa[MAXN],rak[MAXN],d[MAXN];
int s1[MAXN*2],s2[MAXN],ans[MAXN],top=0;
struct edge{
    int x,y,s,e;
};
vector<edge> S;
int n,m,t;

int find(int x){
    while(x!=fa[x]) x=fa[x];
    return x;
}

int getdis(int x){
    int ans=0;
    while(x!=fa[x]) ans^=d[x],x=fa[x];
    return ans;
}

void mearge(int x,int y,int c){
    if(rak[x]>rak[y]){
        s1[++top]=y;s2[top]=1;
        fa[y]=x,d[y]=c;
    }
    else if(rak[y]>rak[x]){
        s1[++top]=x;s2[top]=1;
        fa[x]=y;d[x]=c;
    }
    else{
        rak[x]++;fa[y]=x;
        s1[++top]=y;s2[++top]=0;
        fa[y]=x,d[y]=c;
    }
}

void fenzhi(int l,int r,vector<edge> hh){
    int now=top,mid=(l+r)/2;
    vector<edge> ll,rr;
    for(int i=0,len=hh.size();i<len;i++){
        edge a=hh[i];
        int x=a.x,y=a.y;
        if(a.s==l&&a.e==r){
            int fx=find(x),fy=find(y),c=getdis(x)^getdis(y)^1;
            if(fx!=fy) mearge(fx,fy,c);
            else if(c&1){
                for(int i=l;i<=r;i++) ans[i]=0;
                while(now!=top){
                    if(s2[top]==0) rak[fa[s1[top]]]--;//
                    fa[s1[top]]=s1[top];d[s1[top]]=0;top--;
                }
                return;
            }
        }
        else if(a.e<=mid) ll.push_back(a);
        else if(a.s>mid) rr.push_back(a);
        else{
            edge b=a;
            b.e=mid;ll.push_back(b);
            b=a;b.s=mid+1;
            rr.push_back(b);
        }
    }
    if(l==r) ans[l]=1;
    else fenzhi(l,mid,ll),fenzhi(mid+1,r,rr);
    while(now!=top){
        if(s2[top]==0) rak[fa[s1[top]]]--;//
        fa[s1[top]]=s1[top];d[s1[top]]=0;top--;
    }
}

int main()
{
    scanf("%d%d%d",&n,&m,&t);
    for(int i=1;i<=m;i++){
        int x,y,s,t;scanf("%d%d%d%d",&x,&y,&s,&t);s++;
        if(s<=t) S.push_back((edge){x,y,s,t});
    }
    for(int i=1;i<=n;i++) fa[i]=i,rak[i]=0,d[i]=0;
    for(int i=1;i<=n;i++) ans[i]=1;
    fenzhi(1,t,S);
    for(int i=1;i<=t;i++){
        if(ans[i]) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}

 

BZOJ 4025: 二分图

标签:names   递归   代码   表示   desc   证明   --   自己   return   

原文地址:http://www.cnblogs.com/renjianshige/p/7618107.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!