文章目录
- 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;
}