【洛谷P5787】【模板】线段树分治

线段树分治

题目大意:

给定 $n$ 个点 $m$ 条边的图,有 $k$ 个时刻。

每条边有一个出现时间段,在这个时间段以外这条边不存在。

对每个时刻,输出这个时刻的图是否是二分图。

题解:

线段树分治,用可撤销并查集维护,判断是否有奇环即可。

每个点记录到并查集上父亲的原图上距离的奇偶性。

可以通过这些信息来判断任意两点的奇偶性。

时间复杂度 $O(m\log n\log k)$。

Code:

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int N=1e5+5;
pair<int,int>e[N*2];
vector<int>vec[N<<2];
int n,m,t,fa[N],sz[N],dis[N],sta[N],top;
bool isok=1;
inline int find(int x){while(x!=fa[x])x=fa[x];return x;}
inline int dist(int x){int y=0;while(x!=fa[x])y^=dis[x],x=fa[x];return y;}
void add(int l,int r,int o,const int&L,const int&R,const int&id){
    if(L<=l&&r<=R)vec[o].push_back(id);else{
        const int mid=l+r>>1;
        if(L<=mid)add(l,mid,o<<1,L,R,id);
        if(mid<R)add(mid+1,r,o<<1|1,L,R,id);
    }
}
void solve(int l,int r,int o){
    const int TOP=top;const bool OK_=isok;
    for(int i=0;i<vec[o].size();++i){
        const int id=vec[o][i];
        int u=find(e[id].first),v=find(e[id].second);
        int du=dist(e[id].first),dv=dist(e[id].second);
        if(u!=v){
            if(sz[u]<sz[v])swap(u,v);
            fa[v]=u,sz[u]+=sz[v],dis[v]=du^dv^1;
            sta[++top]=v;
        }else
        if((du^dv^1)&1)isok=0;
    }
    if(l==r)cout<<(isok?"Yes":"No")<<'\n';else{
        const int mid=l+r>>1;
        solve(l,mid,o<<1),solve(mid+1,r,o<<1|1);
    }
    isok=OK_;
    while(top!=TOP){
        int u=sta[top--];
        dis[u]=0,sz[fa[u]]-=sz[u],fa[u]=u;
    }
}
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n>>m>>t;
    for(int i=1;i<=m;++i){
        int l,r;
        cin>>e[i].first>>e[i].second>>l>>r;
        --r;
        add(0,t-1,1,l,r,i);
    }
    for(int i=1;i<=n;++i)fa[i]=i,sz[i]=1;
    solve(0,t-1,1);
    return 0;
}