[bzoj4025] 二分图

题意:

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

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

$n,k\leq 10^{5},m\leq 2\times 10^{5}$。

 

题解:

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

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

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

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

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

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

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

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

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

 

套路:

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

 

代码:

#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\n");
        else printf("No\n");
    }
    return 0;
}
bzoj4025

 

posted @ 2020-06-09 09:59  Fugtemypt  阅读(174)  评论(0编辑  收藏  举报