感觉非常神奇,但大家都说非常套路/kk
把所有操作离线下来,我们以时间为下标建线段树,对于一条边,我们把它加入到它出现的时间区间内去。
我们在线段树上 dfs,每次到达一个节点的时候加边,到达叶子节点的时候输出,回溯的时候撤销(这就是这样做的好处,我们不需要删除,只需要撤销)。
而只有加边和撤销操作的二分图是好维护的:使用可撤销并查集。每次加入一条边时,若两个端点不连通,那么将两个并查集连起来,其中可能需要对一个并查集整体反色,在根上打 tag 即可;若两个端点连通,假如同色则标记答案为 NO,假如不同色则可以忽略它(因为只有撤销操作,所以对于任意时刻,若这条边存在则在这个并查集上的其他边也一定存在,那么这条边就没有贡献了)。
时间复杂度 O ( n log 2 n ) O(n\log ^2n) O(nlog2n)。
#include<bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define mk(a,b) make_pair(a,b)
#define LN 20
#define N 100010
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^'0');
ch=getchar();
}
return x*f;
}
int n,m;
map<pii,int>inst;
vector<pii>e[N<<2];
void update(int k,int l,int r,int ql,int qr,pii now)
{
if(ql<=l&&r<=qr)
{
e[k].push_back(now);
return;
}
int mid=(l+r)>>1;
if(ql<=mid) update(k<<1,l,mid,ql,qr,now);
if(qr>mid) update(k<<1|1,mid+1,r,ql,qr,now);
}
int top;
int fa[N],size[N];
bool half;
bool rev[N];
pii sta[N*LN];
int find(int x)
{
return x==fa[x]?x:find(fa[x]);
}
bool getc(int u)
{
bool col=1;
while(1)
{
if(rev[u]) col^=1;
if(fa[u]==u) break;
u=fa[u];
}
return col;
}
bool insert(int u,int v)
{
int a=find(u),b=find(v);
if(a!=b)
{
if(size[a]<size[b]) swap(a,b);
fa[b]=a,size[a]+=size[b];
sta[++top]=mk(a,b);
if(getc(u)==getc(v)) rev[b]^=1;
return 1;
}
else
{
if(getc(u)==getc(v)) half=0;
return 0;
}
}
void cancel()
{
assert(top);
int a=sta[top].fi,b=sta[top].se;
top--;
size[a]-=size[b],fa[b]=b;
assert(fa[a]==a&&size[a]>=0);
}
void dfs(int k,int l,int r)
{
int ins=0;
bool lst=half;
for(int i=0,s=e[k].size();half&&i<s;i++)
ins+=insert(e[k][i].fi,e[k][i].se);
if(l==r)
{
puts(half?"YES":"NO");
while(ins--) cancel();
return;
}
int mid=(l+r)>>1;
dfs(k<<1,l,mid);
dfs(k<<1|1,mid+1,r);
while(ins--) cancel();
half=lst;
}
int main()
{
n=read(),m=read();
for(int i=1;i<=m;i++)
{
int x=read(),y=read();
if(x>y) swap(x,y);
pii now=mk(x,y);
if(inst[now])
{
update(1,1,m,inst[now],i-1,now);
inst[now]=0;
}
else inst[now]=i;
}
for(auto it:inst)
if(it.second)
update(1,1,m,it.second,m,it.first);
half=1;
for(int i=1;i<=n;i++) fa[i]=i;
dfs(1,1,m);
return 0;
}