/* 转载自 ______________白白の屋 */ #include <iostream> #include <algorithm> #include<string.h> #include<stdio.h> using namespace std; int set[1350005]; int a[125000]; int N,M; int inline find ( int x ) { return x != set[x] ? set[x] = find ( set[x] ) : set[x]; } void inline merge ( int x, int y ) { x = find ( x ); y = find ( y ); if ( x == y ) return ; set[x] = y; } int main () { int ca = 1; while ( scanf ( "%d%d",&N,&M ), M || N ) { for ( int i = 0; i < N; ++ i ) set[i] = i+N; for ( int i = N; i <= N + N + M; ++ i ) set[i] = i; /* 这是关键, 虽然空间的消耗比较大, 但 是节省了大量时间, 这样处理的目的是将 0 -> N-1 的 节点处理成叶子节点 这样在对这些 节点做 S 操作的时候就 不会影响到其他的节点, 而 find 操作 是带路径压缩的, 所以就保证了我们所 有要处理的节点一直是叶子节点 !!!*/ int sep = N + N; int x,y; char ch[5]; for ( int i = 0; i != M; ++ i ) { scanf ( "%s",ch ); switch ( ch[0] ) { case 'M': scanf ( "%d%d",&x,&y ); merge ( x,y ); break; case 'S': scanf ( "%d",&x ); set[x] = sep ++; break; //初始化操作就是为这一步准备的 } } for ( int i = 0; i != N; ++ i ) a[i] = find ( i ); sort ( a, a + N ); int nCount = 1; for ( int i = 1; i < N; ++ i ) if ( a[i] != a[i-1] ) nCount ++; printf("Case #%d: %d\n",ca++,nCount); } return 0; }
这是一题并查集的结合和借点删除的题目,
网上查的资料主要是设置一个虚拟映射数组,来存放要删除的节点,每次对虚拟映射
数组操作,不影响原来的数组,以下贴的代码转自 ______________白白の屋