题意:有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);
}
二分图判定算法
本文介绍了一种通过分治和并查集实现的高效算法,用于动态维护图的二分图性质。面对一系列边的增删操作,算法能快速判断图是否始终保持二分图特性。
2万+

被折叠的 条评论
为什么被折叠?



