CF 813F 可撤销并查集+分治

二分图判定算法
本文介绍了一种通过分治和并查集实现的高效算法,用于动态维护图的二分图性质。面对一系列边的增删操作,算法能快速判断图是否始终保持二分图特性。

传送门
参考YxuanwKeith的博客官方题解

题意:有n个点,q个操作,每次操作在两个点之间连边,若这两点已有边相连,则删去该边,每次操作后判断是否是二分图。

做法:参考的两处讲的很清楚了,主要就是先把查询转化为很多个时间线段,之后在时间上分治即可。主要还是分治、并查集按秩合并以及撤销操作要熟练写好不是那么容易。代码还是很精妙的。

代码:

#include <bits/stdc++.h>
using namespace std;
struct node{
    int u, v, l, r;
}que[105000];
map<pair<int, int>, int>mp;
int n, q;
int fa[105000];
int val[105000];
int dep[105000];
int tot, cnt;
int save[105000<<1];

int getfa(int x){
    if(x==fa[x])return x;
    return getfa(fa[x]);
}

int getval(int x){
    int ans=0;
    while(x!=fa[x]){
        ans^=val[x];
        x=fa[x];
    }
    return ans;
}

void undo(int tag){
    while(cnt!=tag){
        if(save[cnt]<0){
            dep[-save[cnt--]]--;
        }
        else {
            fa[save[cnt]]=save[cnt];
            val[save[cnt]]=0;
            cnt--;
        }
    }
}

void unio(int u, int v, int Val){
    if(dep[u]>dep[v])swap(u, v);
    if(dep[u]==dep[v]){
        dep[v]++;
        save[++cnt]=-v;
    }
    fa[u]=v;
    val[u]=Val;
    save[++cnt]=u;
}

void solve(int L, int R, int num){
    int tag=cnt;
    for(int i=1;i<=num;i++){
        if(que[i].l<=L&&que[i].r>=R){
            int fu=getfa(que[i].u), fv=getfa(que[i].v);
            int valu=getval(que[i].u), valv=getval(que[i].v);
            if(fu==fv&&(valu^valv)==0){
                for(int i=L;i<=R;i++)printf("NO\n");
                undo(tag);
                return;
            }
            unio(fu, fv, valu^valv^1);
            swap(que[i--], que[num--]);
        }
    }
    if(L==R){
        printf("YES\n");
        undo(tag);
        return;
    }
    int mid=(L+R)/2;
    int pnum=num;
    for(int i=1;i<=pnum;i++){
        if(que[i].l>mid)swap(que[i--], que[pnum--]);
    }
    solve(L, mid, pnum);
    pnum=num;
    for(int i=1;i<=pnum;i++){
        if(que[i].r<=mid)swap(que[i--], que[pnum--]);
    }
    solve(mid+1, R, pnum);
    undo(tag);
}

int main()
{
    cin>>n>>q;
    for(int i=1;i<=q;i++){
        int x, y;
        cin>>x>>y;
        if(mp.find(make_pair(x, y))==mp.end()){
            que[++tot]=(node){x, y, i, q};
            mp[make_pair(x, y)]=tot;
        }
        else {
            que[mp[make_pair(x, y)]].r=i-1;
            mp.erase(mp.find(make_pair(x, y)));
        }
    }
    for(int i=1;i<=n;i++)fa[i]=i;
    solve(1, q, tot);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值