題解 CF813F 【Bipartite Checking】
阿新 • • 發佈:2020-07-28
Solution CF813F Bipartite Checking
題目大意:給定一個有\(n\)個點,沒有邊的無向圖。每次操作新增一條邊,如果該邊已存在則刪去這條邊。每次操作之後回答無向圖是否為二分圖
擴充套件域 & 可撤銷並查集、線段樹分治
分析:首先如果只有加入操作,我們可以通過擴充套件域並查集來判斷是否可以構成二分圖
如果一個圖是二分圖,等價於可以對圖進行黑白染色使得每條邊的兩端點顏色都不同
那麼我們對於一個端點\(u\),我們可以另開一個點\(u'\)來表示和它顏色不同的點
如果要加邊\((x,y)\),就合併\(x,y'\),\(x',y\)
如果合併後任意\(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; }