【CF813F】Bipartite Checking(线段树分治+可删除并查集)
文章目錄
- title
- solution
- code
title
You are given an undirected graph consisting of n vertices. Initially there are no edges in the graph. Also you are given q queries, each query either adds one undirected edge to the graph or removes it. After each query you have to check if the resulting graph is bipartite (that is, you can paint all vertices of the graph into two colors so that there is no edge connecting two vertices of the same color).
題意翻譯
給你一個由n個頂點組成的無向圖,最初在圖中沒有邊。同時給你q次查詢,每次查詢時會向圖中添加一個無向邊或者刪除一個無向邊。 在每次查詢之后,您必須檢查結果圖是否為二分圖(在保證沒有連接相同顏色的兩個頂點的邊的條件下,您可以將圖的所有頂點繪制為兩種顏色)
Input
The first line contains two integers n and q (2?≤?n,?q?≤?100000).
Then q lines follow. ith line contains two numbers x i and y i (1?≤?x i?<?y i?≤?n). These numbers describe ith query: if there is an edge between vertices x i and y i, then remove it, otherwise add it.
Output
Print q lines. ith line must contain YES if the graph is bipartite after ith query, and NO otherwise.
Example
Input
3 5
2 3
1 3
1 2
1 2
1 2
Output
YES
YES
NO
YES
NO
solution
線段樹分治的概念打開百度一找一大堆
以時間建線段樹,把區間操作懶標記打在點上,查詢時開始下放修改,回溯的時候把影響改回去
相同的邊如果是第奇數次出現就是加邊,第偶數次出現就是刪邊
因此我們可以排序,把相同的邊排在一起,并且按時間從小到大,假設iii為奇數
這條邊存在的時間就是[t[i],t[i+1])[t[i],t[i+1])[t[i],t[i+1]),如果iii是最后一條邊,就意味著這條邊從該時刻開始會一直存在到最后
把這些邊以存在時間段分別插入線段樹,就跟普通線段樹一樣打懶標記
當然對于某個時刻肯定不止有一條邊,所以就把線段樹的節點開成vectorvectorvector一次性塞進去
完了后就開始查詢,可以知道每一個葉子結點都是一次答案查詢,一路上把懶標記釋放出去
但是為了方便回溯時清除對另外一個查詢無用的影響
我們這里就要使用可刪除并查集,就是記錄在點時放進去的所有操作,然后回溯到該點的時候將所有操作逆著來一遍,回推到初始狀態
具體的看代碼就懂了
code
#include <cstdio> #include <vector> #include<iostream> #include <algorithm> using namespace std; #define MAXN 100005 struct node { int u, v, t; } edge[MAXN]; vector < int > tree[MAXN << 2]; int n, Q, Top; int f[MAXN], st[MAXN], dep[MAXN], color[MAXN];int calc( int x ) { return ( f[x] == x ) ? 0 : calc( f[x] ) ^ color[x]; }void MakeSet() { for( int i = 1;i <= n;i ++ ) f[i] = i; }int findSet( int x ) { return ( x == f[x] ) ? x : findSet( f[x] ); }void Merge( int u, int v, int flag ) {u = findSet( u ), v = findSet( v );if( dep[u] < dep[v] ) swap( u, v );else if( dep[u] == dep[v] ) dep[u] ++, st[++ Top] = -u;f[v] = u, color[v] = flag, st[++ Top] = v; }void regret( int last ) {while( Top > last ) {if( st[Top] < 0 ) dep[-st[Top]] --;else f[st[Top]] = st[Top], color[st[Top]] = 0;Top --;} }bool cmp( node x, node y ) {if( x.u == y.u ) return ( x.v == y.v ) ? x.t < y.t : x.v < y.v;else return x.u < y.u; }void modify( int t, int l, int r, int L, int R, int id ) {if( L <= l && r <= R ) { tree[t].push_back( id ); return; }int mid = ( l + r ) >> 1;if( L <= mid ) modify( t << 1, l, mid, L, R, id );if( mid < R ) modify( t << 1 | 1, mid + 1, r, L, R, id ); }void dfs( int t, int l, int r ) {int now = Top;for( int i = 0;i < tree[t].size();i ++ ) {int u = edge[tree[t][i]].u, v = edge[tree[t][i]].v;int flag = calc( u ) ^ calc( v ) ^ 1;if( findSet( u ) == findSet( v ) ) {if( flag ) {for( int j = l;j <= r;j ++ )printf( "NO\n" );regret( now );return;}}else Merge( u, v, flag );}if( l == r ) {regret( now );printf( "YES\n" );return;}int mid = ( l + r ) >> 1;dfs( t << 1, l, mid );dfs( t << 1 | 1, mid + 1, r );regret( now ); }int main() {scanf( "%d %d", &n, &Q );MakeSet();for( int i = 1, u, v;i <= Q;i ++ ) {scanf( "%d %d", &edge[i].u, &edge[i].v );edge[i].t = i;}sort( edge + 1, edge + Q + 1, cmp );for( int i = 1;i <= Q;i ++ )if( edge[i].u == edge[i + 1].u && edge[i].v == edge[i + 1].v )modify( 1, 1, Q, edge[i].t, edge[i + 1].t - 1, i ), i ++;elsemodify( 1, 1, Q, edge[i].t, Q, i );dfs( 1, 1, Q );return 0; }總結
以上是生活随笔為你收集整理的【CF813F】Bipartite Checking(线段树分治+可删除并查集)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 迎开学水题狂欢赛(舞踏会[dp+三叉树]
- 下一篇: Arm发布IPO以来首份财报 Q2芯片发