想了几中办法,甚至加入了后继结点,但是一直超时,最后参考了网上代码。原文地址:http://www.cppblog.com/MiYu/archive/2010/08/26/124771.html
题目分析:
题目的意思大概就是 有N 封邮件, 编号 0 -> N-1, 然后有2种操作, M : 合并操作, 将 2 种邮件合并为一种.C++ 提交代码.
#include <iostream> #include <algorithm> 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 () { freopen("2473.txt", "r", stdin); int ca = 1; while ( scanf ( "%d%d",&N,&M ), M || N )z { 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; }