Codeforces 813 F. Bipartite Checking 时间分治线段树 and 带权并查集 (可撤销)

题意:不断删边加边,问此时的图可不可以为二分图。

解:判断二分图就看有没与奇数环 (没有就可以,有就不行)

时间分治线段树--->来看当前的图有没有奇环(带权并查集)

写一下并查集那里连边:

merge发生在 x,y的祖先身上,我们是在间接连边,最后查询dis一定是正确的

c[i]表示 点i到 它的root的距离。

所以x,y连边之后merge里yy成为新的root:c[xx]表示xx到yy的距离

len=dis[x]^dis[y]^1

c[xx]==len

void Merge(int x,int y,int len){
    x=find(x),y=find(y);
    if(x==y)return ;
    if(rnk[x]>rnk[y])swap(x,y);
    else if(rnk[x]==rnk[y])++rnk[y],sta[++sp]=-y;
    f[x]=y,c[x]=len,sta[++sp]=x;
}

 

#include<bits/stdc++.h>
#define en '\n'
typedef long long ll;
const ll maxn= 1e5+3;
const int maxm=maxn<<2;
#define pb push_back
using namespace std;
int rd(){int tem;scanf("%d",&tem);return tem;}
map< pair<int,int>,int>ma;
vector<pair<int,int> >tr[maxm];
void update(int x,int l,int r,int ql,int qr,pair<int,int> val){
    if(l>=ql and r<=qr ){tr[x].pb({val});return ; }
    int mid=(l+r)>>1;
    if(ql<=mid)update(x<<1,l,mid,ql,qr,val);
    if(qr>mid)update(x<<1|1,mid+1,r,ql,qr,val);
}
int f[maxn],c[maxn],rnk[maxn],sta[maxn],sp;
int find(int x){for(;x!=f[x];x=f[x]);return x;}
int dis(int x){
    int res=0;for(;f[x] and x!=f[x];x=f[x])res^=c[x];return res;
}
void Merge(int x,int y,int len){
    x=find(x),y=find(y);
    if(x==y)return ;
    if(rnk[x]>rnk[y])swap(x,y);
    else if(rnk[x]==rnk[y])++rnk[y],sta[++sp]=-y;
    f[x]=y,c[x]=len,sta[++sp]=x;
}
void restore(int x){
    for(;sp>x;--sp){
        if(sta[sp]<0)--rnk[sta[sp]];
        else f[sta[sp]]=sta[sp],c[sta[sp]]=0;
    }
}
void ask(int x,int l,int r){
    int now=sp;
    for(int i=0;i<tr[x].size();i++){
        int temx=tr[x][i].first,temy=tr[x][i].second;
        int xx=find(temx),yy=find(temy);
        int len=dis(temx)^dis(temy)^1;
        if(xx==yy){
            if(len&1){
                for(int i=l;i<=r;i++){
                    puts("NO");
                }
                restore(now);
                return ;
            }
        }else Merge(temx,temy,len);
    }
    if(l==r){cout<<"YES"<<en;restore(now);return ;}
    int mid=(l+r)>>1;
    ask(x<<1,l,mid),ask(x<<1|1,mid+1,r),restore(now);
}
signed main()
{
    #ifdef local
    freopen("input2.txt","r",stdin);
    #endif // local
    int n,q;
    n=rd(),q=rd();
    for(int i=1;i<=n;i++)f[i]=i;
    for(int i=1;i<=q;i++){
            int x=rd(),y=rd();
            if(ma[{x,y}]){
                update(1,1,q,ma[{x,y}],i-1,{x,y});
                ma.erase({x,y});
            }
            else  ma[{x,y}]=i;
    }
    for(auto x:ma){
        update(1,1,q,x.second,q,{x.first.first,x.first.second});
    }
    ask(1,1,q);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值