题目大意
给定
N
个点,
Data Constraint
N,M,Q≤200000
题解
有一种用LCT维护最小生成树的方法,然而我并不会。
考虑莫队算法。并查集的插入是非常简单的,但是删除就很难维护。观察莫队算法的性质,对于左端点在同一块的询问,它们的右端点必然是单调的,所以就可以维护右边的并查集,剩下的左边那部分我们就暴力查询,最后再恢复并查集。
直接恢复显然会T。考虑多开一个临时并查集
tp
,记右边维护的并查集为
fa
对于当前的一条边
(x,y)
,我们先在
fa
中找到它们的祖先
fx,fy
,然后在
tp
中找到
fx,fy
的祖先
ffx,ffy
,修改
tp
,最后在记录
tp
哪些位置发生修改,暴力赋为原值即可。这样就避免了对
fa
的修改。
时间复杂度:
O(mm−−√)
SRC
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std ;
#define N 200000 + 10
struct Edge {
int x , y ;
} E[N] ;
struct Query {
int L , R , h ;
} Q[N] ;
bool vis[N] ;
int fa[N] , nowfa[N] , S[N] ;
int Bel[N] , ans[N] ;
int n , m , Qn , Size ;
int Cnt ;
bool cmp( Query a , Query b ) { return Bel[a.L] < Bel[b.L] || ( Bel[a.L] == Bel[b.L] && a.R < b.R ) ; }
int Get( int x ) { return fa[x] == x ? x : fa[x] = Get(fa[x]) ; }
int GetNow( int x ) { return nowfa[x] == x ? x : nowfa[x] = GetNow(nowfa[x]) ; }
int main() {
scanf( "%d%d%d" , &n , &m , &Qn ) ;
Size = sqrt(m) ;
for (int i = 1 ; i <= m ; i ++ ) {
scanf( "%d%d" , &E[i].x , &E[i].y ) ;
Bel[i] = i / Size + (i % Size != 0) ;
}
for (int i = 1 ; i <= Qn ; Q[i].h = i , i ++ ) scanf( "%d%d" , &Q[i].L , &Q[i].R ) ;
sort( Q + 1 , Q + Qn + 1 , cmp ) ;
int L = 0 , R = 0 ;
for (int i = 1 ; i <= Qn ; i ++ ) {
if ( Bel[Q[i].L] != Bel[L] ) {
Cnt = n ;
int st = Size * Bel[Q[i].L] + 1 ;
for (int k = 1 ; k <= n ; k ++ ) fa[k] = nowfa[k] = k ;
for (int k = st ; k <= Q[i].R ; k ++ ) {
int fx = Get(E[k].x) ;
int fy = Get(E[k].y) ;
if ( fx == fy ) continue ;
Cnt -- ;
fa[fx] = fy ;
}
} else {
int st = max( Size * Bel[Q[i].L] + 1 , R ) ;
for (int k = st ; k <= Q[i].R ; k ++ ) {
int fx = Get(E[k].x) ;
int fy = Get(E[k].y) ;
if ( fx == fy ) continue ;
Cnt -- ;
fa[fx] = fy ;
}
}
S[0] = 0 ;
int ori = Cnt ;
int ed = min( Q[i].R , Size * Bel[Q[i].L] ) ;
for (int k = Q[i].L ; k <= ed ; k ++ ) {
int fx = Get(E[k].x) ;
int fy = Get(E[k].y) ;
if ( fx == fy ) continue ;
int ffx = GetNow(fx) ;
int ffy = GetNow(fy) ;
if ( ffx == ffy ) continue ;
Cnt -- ;
nowfa[ffx] = ffy ;
if ( !vis[ffx] ) vis[ffx] = 1 , S[++S[0]] = ffx ;
}
while ( S[0] ) {
nowfa[S[S[0]]] = S[S[0]] ;
vis[S[S[0]]] = 0 ;
S[0] -- ;
}
ans[Q[i].h] = Cnt ;
Cnt = ori ;
L = Q[i].L , R = Q[i].R ;
}
for (int i = 1 ; i <= Qn ; i ++ ) printf( "%d\n" , ans[i] ) ;
return 0 ;
}
以上.