zoukankan      html  css  js  c++  java
  • [bzoj4025] 二分图

    题意:

    有一张n个点的图,有m条边,每条边只在$[l,r]$时段内存在。

    请你对每个时刻求出此时这张图是否是二分图。

    $n,kleq 10^{5},mleq 2 imes 10^{5}$。

    题解:

    首先二分图的充要条件是没有奇环。(不一定非得连通)

    那么有一个套路的带权并查集判二分图的做法(不考虑时间的限制):

    按任意顺序加边,对于一条边$(u,v)$:

    • 如果u,v不连通,直接加入。
    • 否则,求一下u到v路径长度的奇偶性:
    1. 如果是偶数那么加了这条边形成了一个奇环,所以这张图已经是二分图了。
    2. 如果是奇数那么可以忽略这条边,因为用当前路径$u ightarrow v$可以在任何一个环中替代边$(u,v)$。(重复路径不影响环的奇偶性)

    用带权并查集维护每个点在并查集里的父亲、它到父亲路径长度的奇偶性。

    注意在合并u,v的时候我们实际上合并的是$f_{u},f_{v}$,而这两个点之间的路径应该是$f_{u} ightarrow u ightarrow v ightarrow f_{v}$,所以更新路径长度时需要暴力从u,v往上跳。

    回到这道题,我们按线段树分治的经典套路,把操作挂到线段树节点上。

    本来可以路径压缩,但离开一个节点时需要撤销操作,于是不能破坏父子关系,只能按秩合并(把siz小的合并到siz大的上)。

    复杂度$O(nlog^{2}{n})$,用LCT可以做到$O(nlog{n})$,不过……

    套路:

    • 判二分图$ ightarrow$黑白染色(适用面较窄)或带权并查集。
    • 支持时间区间上的可撤销操作和查询某时刻的状态$ ightarrow$线段树分治。

    代码:

    #include<bits/stdc++.h>
    #define maxn 100005
    #define maxm 500005
    #define inf 0x7fffffff
    #define ll long long
    #define rint register int
    #define mp make_pair
    #define debug(x) cerr<<#x<<": "<<x<<endl
    #define fgx cerr<<"--------------"<<endl
    #define dgx cerr<<"=============="<<endl
    
    using namespace std;
    vector<pair<int,int> > tr[maxn<<2],ok[maxn<<2];
    int n,m,k,F[maxn],D[maxn],ans[maxn],siz[maxn];
    
    inline int read(){
        int x=0,f=1; char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        return x*f;
    }
    
    inline int getD(int x){return F[x]==x?0:D[x]^getD(F[x]);}
    inline int find(int x){return F[x]==x?x:find(F[x]);}
    
    inline void upd(int u,int v,int x,int y,int l,int r,int k){
        if(x<=l && r<=y){tr[k].push_back(mp(u,v));return;}
        int mid=l+r>>1;
        if(x<=mid) upd(u,v,x,y,l,mid,k<<1);
        if(y>mid) upd(u,v,x,y,mid+1,r,k<<1|1);
    }
    
    inline void solve(int l,int r,int op,int k){
        if(op){
            for(int i=0;i<tr[k].size();i++){
                int u=tr[k][i].first,v=tr[k][i].second;
                int t1=find(u),t2=find(v);
                if(t1!=t2){
                    if(siz[t1]<siz[t2]) swap(u,v),swap(t1,t2);    
                    int d1=getD(u),d2=getD(v);
                    ok[k].push_back(mp(t1,t2));
                    F[t2]=t1,D[t2]=d1^d2^1,siz[t1]+=siz[t2];
                }
                else if(!(getD(u)^getD(v))) op=0;
            }
        }
        if(l==r) ans[l]=op;
        int mid=l+r>>1;
        if(l!=r) solve(l,mid,op,k<<1),solve(mid+1,r,op,k<<1|1);
        for(int i=ok[k].size()-1;i>=0;i--){
            int t1=ok[k][i].first,t2=ok[k][i].second;
            F[t2]=t2,D[t2]=0,siz[t1]-=siz[t2];
        }
    }
    
    int main(){
        n=read(),m=read(),k=read();
        for(int i=1;i<=m;i++){
            int u=read(),v=read(),l=read()+1,r=read();
            upd(u,v,l,r,1,k,1);
        } 
        for(int i=1;i<=n;i++) F[i]=i,D[i]=0,siz[i]=1;
        solve(1,n,1,1);
        for(int i=1;i<=k;i++){
            if(ans[i]) printf("Yes
    ");
            else printf("No
    ");
        }
        return 0;
    }
    bzoj4025
  • 相关阅读:
    datagrid表格宽度设置成100%,宽度却不是100%,反而很窄
    Spring MVC 和Struts2对比
    在不知道用户名密码的情况下登录oracle数据库
    IE浏览器的兼容性问题
    在spring mvc 中读取配置文件(porperties)的信息
    godaddy设置方法 控制面板
    easyui datagrid 动态改变大小
    15 个最佳 jQuery 翻书效果插件
    windows7 如何关闭开机启动讲述人
    存储过程代码审核
  • 原文地址:https://www.cnblogs.com/YSFAC/p/13070729.html
Copyright © 2011-2022 走看看