zoukankan      html  css  js  c++  java
  • 题解 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;
    }
    
  • 相关阅读:
    css盒子模型之内边距padding及简写
    css盒子模型之宽度和高度
    windows 组策略
    windows 快捷键
    cmd 命令快捷键
    django 远程访问
    django 部署在 apache2 上面
    国内常用开源镜像站
    ubuntu1804自带官方源
    ubuntu1604 apt华为国内源
  • 原文地址:https://www.cnblogs.com/colazcy/p/13393866.html
Copyright © 2011-2022 走看看