题解 CF813F Bipartite Checking

Posted colazcy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解 CF813F Bipartite Checking相关的知识,希望对你有一定的参考价值。

题目链接

Solution CF813F Bipartite Checking

题目大意:给定一个有(n)个点,没有边的无向图。每次操作添加一条边,如果该边已存在则删去这条边。每次操作之后回答无向图是否为二分图

扩展域 & 可撤销并查集、线段树分治


分析:首先如果只有加入操作,我们可以通过扩展域并查集来判断是否可以构成二分图

如果一个图是二分图,等价于可以对图进行黑白染色使得每条边的两端点颜色都不同

那么我们对于一个端点(u),我们可以另开一个点(u‘)来表示和它颜色不同的点

如果要加边((x,y)),就合并(x,y‘)(x‘,y)

如果合并后任意(u)(u‘)在一个集合内此图都不是二分图

原题带撤销,我们没办法快速从并查集上任意删除一条边,但是可以(O(1))撤销最后的一次修改

因此我们可以采用线段树分治的方法

传统的线段树维护序列,这里维护时间。由于加边删边成对出现(我们认为在时刻(q + 1)删去所有剩余边),可以利用线段树的区间修改方便的加入操作

单点查询一个时间点,我们可以取得一系列操作,依次执行便可以得到一个时刻的答案

如果暴力将父节点操作推给子节点,复杂度爆炸(没法(O(1)pushdown))。因此我们采用标记永久化的方式。不下传标记,用vector记录会影响一个时间段的所有操作,一路走一路累加影响,回溯的时候撤销

对于统计一条边的出现时间段,std::map可以做到

#include <cstdio>
#include <cstring>
#include <utility>
#include <map>
#include <stack>
#include <vector>
using namespace std;
const int maxn = 1e5 + 100;
inline int read(){
	int x = 0;char c = getchar();
	while(!isdigit(c))c = getchar();
	while(isdigit(c))x = x * 10 + c - ‘0‘,c = getchar();
	return x;
}
struct mpair{int fir,sec;};
int n,q,ans[maxn];
map<int,int> mp[maxn];
namespace mset{
	int f[maxn << 1],siz[maxn << 1];
	inline void init(){
		for(int i = 1;i <= 2 * n;i++)f[i] = i,siz[i] = 1;
	}
	inline int find(int x){while(f[x] != x)x = f[x];return x;}
	inline mpair merge(int x,int y){
		x = find(x),y = find(y);
		if(siz[x] > siz[y])swap(x,y);
		if(x == y)return mpair{-1,-1};
		f[x] = y;
		siz[y] += siz[x];
		return mpair{x,y};
	}
}
namespace seg{
	vector<mpair> vec[maxn << 2];
	#define ls (rt << 1)
	#define rs (rt << 1 | 1)
	inline void modify(int a,int b,mpair v,int l = 1,int r = q,int rt = 1){
		if(a <= l && b >= r){
			vec[rt].push_back(v);
			return;
		}
		int mid = (l + r) >> 1;
		if(a <= mid)modify(a,b,v,l,mid,ls);
		if(b >= mid + 1)modify(a,b,v,mid + 1,r,rs);
	}
	stack<mpair> stk;
	inline void dfs(int rt = 1,int l = 1,int r = q){
		int t = stk.size(),flag = 1;
		for(auto x : vec[rt]){
			mpair res = mset::merge(x.fir,x.sec + n);
			stk.push(res);
			res = mset::merge(x.fir + n,x.sec);
			stk.push(res);
			if(mset::find(x.fir) == mset::find(x.fir + n) || mset::find(x.sec) == mset::find(x.sec + n)){
				flag = 0;
				break;
			}
		}
		if(l == r)ans[l] = flag;
		else if(flag){
			int mid = (l + r) >> 1;
			dfs(ls,l,mid);
			dfs(rs,mid + 1,r);
		}
		while(stk.size() != t){
			int x = stk.top().fir,y = stk.top().sec;
			mset::siz[y] -= mset::siz[x];
			mset::f[x] = x;
			stk.pop();
		}
	}
	#undef ls
	#undef rs
}
int main(){
	n = read(),q = read();mset::init();
	for(int x,y,i = 1;i <= q;i++){
		x = read(),y = read();
		if(mp[x][y])seg::modify(mp[x][y],i - 1,mpair{x,y}),mp[x][y] = 0;
		else mp[x][y] = i;
	}
	for(int i = 1;i <= n;i++)
		for(auto x : mp[i])
			if(x.second)seg::modify(x.second,q,mpair{i,x.first});
	seg::dfs();
	for(int i = 1;i <= q;i++)puts(ans[i] ? "YES" : "NO");
	return 0;
}

以上是关于题解 CF813F Bipartite Checking的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces 1630 E Making It Bipartite 题解 (Dilworth定理)

题解 CF817C Really Big Numbers

Aiiage Camp Day3 B Bipartite

CodeForces901 C. Bipartite Segments

CF1132D Stressful Training

CF1056E Check Transcription 字符串哈希