这个题显然是用并查集,主要是涉及到并查集中结点的删除,一般而言构造的并查集要独立出结点,直观的想法是直接修改它的父结点为自己,但这样会出现错误。它原来的子结点也跟着一起独立出来了,这和题目是不符合的(题目只要求独立出要独立的那个,它的孩子结点还是应该在本来的那棵树上),考察合并函数发现,合并时总是把一个集合的根结点作为另一个集合的根结点。之所以不好把某个结点直接独立出来是因为它本身还是别的点的父结点,而这种情况出现的原因就是自己做过根结点。为保证编号为0-n-1的结点永远只做叶子结点,只需要给它们找个虚拟的根结点即可,也就是说,当某个结点只含自己时的初试状态不是p[x] = x,而是p[x] = 虚拟根结点编号。
这样要独立出某个结点时,直接修改某结点的p[x] = 虚拟根结点编号(该虚拟根结点自己也是一个独立的集合)
这里还有的一个技巧是因为getchar()速度快于scanf()函数,因为有大量数据的读入,所以自己用getchar()写一个scan函数用来读入能省不少时间,用scanf跑了1200ms用scan只读了300ms
以上是校队讨论时lwl同学的讲解`````
View Code
1 #include <cstdio> 2 #include <cstring> 3 #define MAXN 100005+1000000 4 int p[MAXN]; 5 int n,m; 6 int find(int x) 7 { 8 return p[x] == x ? x : p[x] = find(p[x]); 9 } 10 void Union(int x,int y) 11 { 12 int r1=find(x); 13 int r2=find(y); 14 p[r1] = r2; 15 } 16 void scan(int &x) 17 { 18 char c; 19 while(c=getchar(),c<'0'||c>'9'); 20 x = c-'0'; 21 while(c=getchar(),(c>='0'&&c<='9')) 22 x = x*10+c-'0'; 23 } 24 int main() 25 { 26 // freopen("out.cpp","r",stdin); 27 int ser = 0; 28 while(1) 29 { 30 scan(n); 31 scan(m); 32 if(n==0 && m==0) break; 33 for(int i=0; i<n; ++i) 34 p[i] = n+i; 35 for(int i=n; i<MAXN; ++i) 36 p[i] = i; 37 int ptr = 2*n; 38 while(m--) 39 { 40 char c; 41 while(c= getchar(),c!='M'&&c!='S'); 42 if(c =='M') 43 { 44 int x,y; 45 scan(x); 46 scan(y); 47 if(find(x) != find(y)) 48 Union(x,y); 49 } 50 else 51 { 52 int h; 53 scan(h); 54 p[h] = ptr++; 55 } 56 } 57 int ans= 0; 58 bool used[MAXN]; 59 memset(used,0,sizeof(used)); 60 for(int i=0; i<n; ++i) 61 { 62 int l = find(i); 63 if(!used[l]) 64 { 65 used[l] =1; 66 ++ans; 67 } 68 } 69 printf("Case #%d: %d\n",++ser,ans); 70 } 71 return 0; 72 }
贴代码: