题目
Luogu
2
≤
n
,
q
≤
1
0
5
2\le n,q\le 10^5
2≤n,q≤105
思路
通过带权并查集判断二分图真是妙(以前没见过)
首先我们能找到每条边的出现时间
[
l
i
,
r
i
]
[l_i,r_i]
[li,ri] ,那么线段树分治后
发现是一个区间修改,单点查询的样子,修改标记永久化即可
然后就只剩下如何处理加边和删边的问题了
然后发现好像网上都把判断二分图当作众所周知…
我们使用带权并查集来解决这个问题,具体而言定义
f
a
[
u
]
=
u
fa[u]=u
fa[u]=u 的节点颜色为
0
0
0 ,然后每个点上有一个标记
c
u
c_u
cu 表示和
f
a
[
u
]
fa[u]
fa[u] 的颜色异同关系,显然一个点的颜色就是它到根的
c
u
c_u
cu 异或和
可以用线段树懒标记类比
一次
(
u
,
v
)
(u,v)
(u,v) 的加边操作如何实现?
如果
(
u
,
v
)
(u,v)
(u,v) 不连通,我们首先得到
u
,
v
u,v
u,v 各自颜色,如果不同那么并查集合并表示连通性,颜色相同将其中一个并查集顶端颜色变换连接即可
如果
(
u
,
v
)
(u,v)
(u,v) 连通,那么两个点如果同色就不合法
然后删除操作我们用回撤即可
相关代码
void Merge(int u,int v,int flag){
u=Find(u),v=Find(v);
if(u==v) return ;
if(dep[u]<dep[v]) swap(u,v);
else if(dep[u]==dep[v])
dep[u]++,Stk[++tp]=-u;
fa[v]=u,c[v]=flag,Stk[++tp]=v;
return ;
}
void Restore(int ntp){
while(tp>ntp){
if(Stk[tp]<0) dep[-Stk[tp]]--;
else fa[Stk[tp]]=Stk[tp],c[Stk[tp]]=0;
tp--;
}
return ;
}
其实用并查集拓展域也能实现,只不过不怎么流行…
注意这里线段树分治是统一处理询问优化的时间复杂度
代码
#include<map>
#include<set>
#include<stack>
#include<queue>
#include<cmath>
#include<cstring>
#include<climits>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
//#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
//char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
inline LL read() {
bool f=0;LL x=0;char c=getchar();
while(c<'0'||'9'<c){if(c==EOF)exit(0);if(c=='-')f=1;c=getchar();}
while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return !f?x:-x;
}
#define MAXN 100000
#define INF 100000000000000ll
int Stk[MAXN+5],tp;
int fa[MAXN+5],c[MAXN+5],dep[MAXN+5];
int Find(int u){return fa[u]==u?u:Find(fa[u]);}
int Dis(int u){return fa[u]==u?0:(Dis(fa[u])^c[u]);}
void Merge(int u,int v,int flag){
u=Find(u),v=Find(v);
if(u==v) return ;
if(dep[u]<dep[v]) swap(u,v);
else if(dep[u]==dep[v])
dep[u]++,Stk[++tp]=-u;
fa[v]=u,c[v]=flag,Stk[++tp]=v;
return ;
}
void Restore(int ntp){
while(tp>ntp){
if(Stk[tp]<0) dep[-Stk[tp]]--;
else fa[Stk[tp]]=Stk[tp],c[Stk[tp]]=0;
tp--;
}
return ;
}
#define lch (rt<<1)
#define rch (rt<<1|1)
#define mp make_pair
#define pii pair<int,int>
vector<pii >tree[5*MAXN+5];
void Insert(int rt,int L,int R,int qL,int qR,pii x){
if(qL<=L&&R<=qR){
tree[rt].push_back(x);
return ;
}
int Mid=(L+R)>>1;
if(qL<=Mid)
Insert(lch,L,Mid,qL,qR,x);
if(Mid+1<=qR)
Insert(rch,Mid+1,R,qL,qR,x);
return ;
}
void DFS(int rt,int L,int R){
int ntp=tp;
for(int i=0;i<(int)tree[rt].size();i++){
int u=tree[rt][i].first,v=tree[rt][i].second;
int flag=Dis(u)^Dis(v)^1;//同色变色
if(Find(u)==Find(v)){
if(flag){
for(int j=L;j<=R;j++)
puts("NO");
Restore(ntp);
return ;
}
}
else Merge(u,v,flag);
}
if(L==R){
puts("YES");
Restore(ntp);
return ;
}
int Mid=(L+R)>>1;
DFS(lch,L,Mid),DFS(rch,Mid+1,R);
Restore(ntp);
return ;
}
map<pii,int> Map;
int main(){
int n=read(),q=read();
for(int i=1;i<=n;i++)
fa[i]=i;
for(int i=1;i<=q;i++){
int u=read(),v=read();
if(Map.count(mp(u,v)))
Insert(1,1,q,Map[mp(u,v)],i-1,mp(u,v)),Map.erase(mp(u,v));
else Map[mp(u,v)]=i;
}
for(map<pii,int>::iterator it=Map.begin();it!=Map.end();it++)
Insert(1,1,q,it->second,q,it->first);
DFS(1,1,q);
return 0;
}