您的位置:首页 > 其它

Uva11987 Almost Union-Find 并查集带删除

2017-11-27 21:18 288 查看

大家都很强, 可与之共勉。

题意:

搞一个数据结构,支持如下操作,每组数据共有m个操作:

1 p q:合并p和q所在集合,如果已经在一个集合中,忽略此命令

2 p q:把p移动到q所在集合,如果已经在一个集合中,忽略此命令

3 p:输出p所在集合的元素个数和该集合所有元素之和

题解:

建立每个点一个虚点,设为根,每个点的初始祖先即为虚点。然后合并集合是合并初始祖先。移动单个元素就直接把实点到父亲的根切断,然后直接改父亲,计算贡献。这样一定有一个性质就是一颗树上叶子结点一定是实点。

注意有多组数据

# include <bits/stdc++.h>

inline int read ( )  {
register int x, c ;
while ( isspace ( c = getchar ( ) ) ) ;
for ( x = -48 + c ; isdigit ( c = getchar ( ) ) ; ( x *= 10 ) += c - 48 ) ;
return x ;
}

# define N 100010

class UnionFindSet  {
private :
int fa [N << 1] ;
public :
UnionFindSet ( )  {     }
UnionFindSet ( int n )  {
for ( register int i = 1 ; i <= n ; ++ i )  fa [i] = fa [n + i] = n + i ;
}
inline int find ( int x )  {
while ( x ^ fa [x] )  x = fa [x] = fa [fa [x]] = fa [fa [fa [x]]] ;
return x ;
}
inline void join ( int u, int v )  {
fa [find ( u )] = find ( v ) ;
}
inline void changefa ( int u, int p )  {
fa [u] = p ;
}
} T ;

int siz [N << 1], sum [N << 1] ;

# undef N

int main ( )  {
//  freopen ( "in.txt", "r", stdin ) ;
//  freopen ( "out.txt", "w", stdout ) ;

int n, m ;
while ( ~ scanf ( "%d%d", & n, & m ) )  {
T = UnionFindSet ( n ) ;
for ( int i = n + 1 ; i <= n + n ; ++ i )  siz [i] = 1, sum [i] = i - n ;

while ( m -- )  {
int opt ( read ( ) ) ;
switch ( opt )  {
case 1 :  {
int p ( read ( ) ), q ( read ( ) ), fp, fq ;
if ( ( fp = T.find ( p ) ) ^ ( fq = T.find ( q ) ) )  {
T.join ( p, q ) ;
siz [fq] += siz [fp] ;
sum [fq] += sum [fp] ;
}
break ;
}
case 2 :  {
int p ( read ( ) ), q ( read ( ) ) ;
if ( T.find ( p ) == T.find ( q ) )  continue ;
-- siz [T.find ( p )], sum [T.find ( p )] -= p ;
T.changefa ( p, T.find ( q ) ) ;
siz [T.find ( q )] += 1 ;
sum [T.find ( q )] += p ;
break ;
}
case 3 :  {
int u ( read ( ) ) ;
printf ( "%d %d\n", siz [T.find ( u )], sum [T.find ( u )] ) ;
break ;
}
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: