【XSY2506】bipartite(动态维护二分图,线段树,可撤销并查集)

感觉非常神奇,但大家都说非常套路/kk

把所有操作离线下来,我们以时间为下标建线段树,对于一条边,我们把它加入到它出现的时间区间内去。

我们在线段树上 dfs,每次到达一个节点的时候加边,到达叶子节点的时候输出,回溯的时候撤销(这就是这样做的好处,我们不需要删除,只需要撤销)。

而只有加边和撤销操作的二分图是好维护的:使用可撤销并查集。每次加入一条边时,若两个端点不连通,那么将两个并查集连起来,其中可能需要对一个并查集整体反色,在根上打 tag 即可;若两个端点连通,假如同色则标记答案为 NO,假如不同色则可以忽略它(因为只有撤销操作,所以对于任意时刻,若这条边存在则在这个并查集上的其他边也一定存在,那么这条边就没有贡献了)。

时间复杂度 \(O(n\log ^2n)\)

#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;
}
posted @ 2022-10-30 10:40  ez_lcw  阅读(21)  评论(0编辑  收藏  举报