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
#include
#include
#include
#include
#include
#include
#include
#include
#include
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
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;
}