[BZOJ 4025] 二分图

题面

4025: 二分图


Solution

这种每条边出现在一段区间的题,我们可以先考虑使用线段树分治来搞。

这题我们考虑离线下来搞。离线之后,我们会发现,某条边会在某些询问区间中出现
考虑以询问的编号为下标建线段树,我们把每一条边出现的时间段全部加到线段树里面去。

接下来,我们考虑如何维护一个图是否为二分图的问题。我们知道,一个图是二分图当且仅当这个图上所有的环的长度(点的个数)均为偶数
知道这一点之后,我们考虑用并查集处理这个问题。对于没有环的时候,我们直接连上就好。对于有环的时候,我们就需要知道这两个点的距离是奇数还是偶数。如果是奇数的,说明之后无论在怎么连,这个图一定gg了,直到这条边取消位置;如果是偶数的,说明这个环是合法的,之后无论有什么边接到这个环上,我们当前这条新插入的边都不会有任何影响。因此,如果一条边会连城环,无论如何,这条边都不会被加上

所以说,我们这里维护的东西是一个生成树。我们考虑用并查集维护这个生成树。因为我们这里是按秩合并的并查集,失去了原来的树的结构。因此,我们考虑在每个点上维护它到它的父亲的真实距离。我们在连接两个点的时候,我们在并查集上肯定是连接这两个点所在并查集的父亲。这时候,这两个父亲的真实距离一定为这两个点到父亲的真实距离和+1,(这里我们的真实距离和直接暴力算即可,因为并查集按秩合并时,高度不会太大)。

我们查询的时候我们算的是它们两个点到并查集的LCA的真实距离,因为并查集的LCA不一定是它们在原树中的LCA,因此我们这里按真实距离算出来的距离并非它们两的真实距离。但是,我们会发现这里多算的距离一定是2的倍数(两个点同时经过一条边),因此,我们的奇偶性质还是正确的

时间复杂度$O(nlog^2n)$
就酱,我们就可以切掉这道题啦ヾ(◍°∇°◍)ノ゙


Code

//BZOJ 4025 二分图
//线段树二分+并查集妙题
//Mar,25th,2019
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
long long read()
{
    long long x=0,f=1; char c=getchar();
    while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
    while(isdigit(c)){x=x*10+c-'0';c=getchar();}
    return x*f;
}
const int N=100000+100;
struct edge
{
    int s,t;
    edge (int x,int y)
    {
        s=x,t=y;
    }
};
struct UnF
{
    int fa[N],size[N],dis[N],depth[N],top,mstack[N*20];
    void Init(int n)
    {
        for(int i=1;i<=n;i++)
            depth[i]=size[i]=1;
    }
    int GetFa(int x,int &w)
    {
        if(fa[x]==0) 
        {
            w++;
            return x;
        }
        int t=GetFa(fa[x],w+=dis[x]+1);
        depth[x]=depth[fa[x]]+1;
        return t;
    }
    int LCA(int x,int y,int &w)
    {
        w=1;
        if(depth[x]<depth[y]) swap(x,y);
        while(depth[x]>depth[y])
            w+=dis[x]+1,x=fa[x];
        if(x==y) return x;
        w++;
        while(fa[x]!=fa[y])
        {
            w+=dis[x]+1,w+=dis[y]+1;
            x=fa[x],y=fa[y];
        }
        w+=dis[x]+dis[y]+1;
        return fa[x];
    }
    int Link(int x,int y)
    {
        int d1=0,d2=0,fa1=GetFa(x,d1),fa2=GetFa(y,d2);
        if(size[fa1]>size[fa2])
            swap(x,y),swap(fa1,fa2),swap(d1,d2);
        mstack[++top]=0;
        //cerr<<fa1<<" "<<fa2<<" "<<x<<" "<<y<<" "<<d1<<" "<<d2<<endl;
        if(fa1==fa2)
        {
            int t_d=0;
            LCA(x,y,t_d);
            if(t_d%2==1)
                return 1;
            return 0;
        }
        mstack[top]=fa1;
        dis[fa1]=d1+d2-2,fa[fa1]=fa2,size[fa2]+=size[fa1];
        return 0;
    }
    void Undo()
    {
        if(mstack[top]!=0)
            size[fa[mstack[top]]]-=size[mstack[top]],fa[mstack[top]]=0,
            dis[mstack[top]]=0,depth[mstack[top]]=1;
        top--;
    }
}unf;
int ans[N],top,OK;
struct SegmentTree
{
    #define mid ((now_l+now_r)>>1)
    #define lson (now<<1)
    #define rson (now<<1|1)
    vector <edge> w[N<<2];
    void Add(int l,int r,edge x,int now,int now_l,int now_r)
    {
        if(now_l>=l and now_r<=r)
        {
            w[now].push_back(x);
            return;
        }
        if(l<=mid) Add(l,r,x,lson,now_l,mid);
        if(r>mid) Add(l,r,x,rson,mid+1,now_r);
    }
    void dfs(int now,int now_l,int now_r)
    {
        int cnt=0;
        for(int i=0;i<int(w[now].size());i++)
            cnt+=unf.Link(w[now][i].s,w[now][i].t);
        OK+=cnt;
        //cerr<<endl;
        //cerr<<now<<" "<<now_l<<" "<<now_r<<" "<<cnt<<endl;
        if(now_l==now_r)
            ans[now_l]=OK;
        else
        {
            dfs(lson,now_l,mid);
            dfs(rson,mid+1,now_r);
        }
        OK-=cnt;
        for(int i=0;i<int(w[now].size());i++)
            unf.Undo();
    }
    #undef mid
    #undef lson
    #undef rson
}sgt;
int n,m,q;
int main()
{
    n=read(),m=read(),q=read();
    unf.Init(n);
    for(int i=1;i<=m;i++)
    {
        int u=read(),v=read(),s=read(),t=read();
        sgt.Add(s+1,t,edge(u,v),1,1,q);
    }
     
    sgt.dfs(1,1,q);
     
    for(int i=1;i<=q;i++)
        if(ans[i]==0)
            printf("Yes\n");
        else
            printf("No\n");
    return 0;
}
点赞

发表评论

电子邮件地址不会被公开。必填项已用 * 标注