题解 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定理)