比较好的并查集!
思路:
将每个星球看成一个点,以太隧道为边,建成一个图的模型!
O(q)每一次询问对于一个点,加上O(m)枚举删除的每一条边,O(n)暴力 Tarjan(),时间复杂度为 O(nq + m);
稳 T 掉,懒得试多少分 ……
想了一下,求连通块的方法除了 Tarjan(),还有并查集 。但是并查集也不支持快速删除啊 ……
但是并查集可以加点判断连通块 …… 对!如果从最后一次删除开始添加每一个点,这样只需要枚举与该点连接的边,然后判断这条边连接的另一个点是否在这个图中,注意,有些点可能还没有加进来,所以合并并查集的条件就是必须另一点需要被添加在图中。
对于幸免,也就是没有被删掉的点,他们一开始就是在这个图中的,所以需要预处理出在已经在图中的点,先将他们加入重构的图中,然后再反着询问顺序加入被删除的点。
我其实一开始有一个疑问:
如果将这某个点加进去,那么它相邻的每一条边也就存在了,然而又需要开一个边的集合存这个点所连接的边,因为必须要保证每一条边都在才能保证不漏掉它的贡献 ……
被卡住了,怎么写?
其实这个疑问是多余的,因为如果某一条边都会被枚举到,被枚举到时,如果这时它能使连通块个数减一,那么一定会产生贡献,否则不会产生任何贡献 。枚举并不会漏掉任何一条边,并且任何一条边的贡献也不会出差错 。
所以这个疑问是多余的 。
代码:
#include <bits/stdc++.h>
const int N = 400000 + 5 ;
int head [ N ] , nxt [ N ] , to [ N ] , cn ;
int tim [ N ] , use [ N ] , num [ N ] , fa [ N ] ;
int n , m , x , y , k , ans ;
int find ( int x ) {
if ( fa [ x ] == x ) return x ;
return fa [ x ] = find ( fa [ x ] ) ;
}
void create ( int , int ) ;
void init ( ) ;
void work ( ) ;
void print ( ) ;
int main ( ) {
scanf ( "%d%d" , & n , & m ) ;
for ( int i = 1 ; i <= m ; i ++ ) {
scanf ( "%d%d" , & x , & y ) ;
create ( x , y ) ;
create ( y , x ) ;
}
scanf ( "%d" , & k ) ;
for ( int i = 1 ; i <= k ; i ++ ) {
scanf ( "%d" , & tim [ i ] ) ;
use [ tim [ i ] ] = true ;
}
init ( ) ;
work ( ) ;
print ( ) ;
return 0 ;
}
void create ( int u , int v ) {
cn ++ ;
to [ cn ] = v ;
nxt [ cn ] = head [ u ] ;
head [ u ] = cn ;
}
void init ( ) {
for ( int i = 0 ; i < n ; i ++ )
fa [ i ] = i ;
for ( int i = 0 ; i < n ; i ++ ) {
if ( use [ i ] ) continue ;
ans ++ ;
for ( int j = head [ i ] ; j ; j = nxt [ j ] ) {
int v = to [ j ] ;
if ( use [ v ] ) continue ;
int tmp1 = find ( i ) , tmp2 = find ( v ) ;
if ( tmp1 != tmp2 ) {
fa [ tmp2 ] = tmp1 ;
ans -- ;
}
}
}
num [ k + 1 ] = ans ;
}
void work ( ) {
for ( int i = k ; i >= 1 ; i -- ) {
int u = tim [ i ] ;
ans ++ ;
use [ u ] = false ;
for ( int j = head [ u ] ; j ; j = nxt [ j ] ) {
int v = to [ j ] ;
if ( use [ v ] ) continue ;
int tmp1 = find ( u ) , tmp2 = find ( v ) ;
if ( tmp1 != tmp2 ) {
fa [ tmp2 ] = tmp1 ;
ans -- ;
}
}
num [ i ] = ans ;
}
}
void print ( ) {
for ( int i = 1 ; i <= k + 1 ; i ++ )
printf ( "%d\n" , num [ i ] ) ;
}