感觉有些难的题,刚开始就想到了设立虚节点,但是实现总是出错,因为每次设立了虚节点之后,无法将原节点和虚节点分开,导致虚节点根本无意义。
以上纯属废话,可以忽略……
题意——
给定n个点(0, 1, 2, ..., n-1),可进行两种操作:1. 将两个点合并到一个集合中; 2. 将一个点从原有集合中取出。问最后点有几个集合。
很明显的并查集,包含合并,删除操作。
但是,删除某节点的时候,需要保证这个集合中,除了被删除节点的其它节点不变,这点有些难以处理。
我们知道,并查集其实是一棵棵树,我们将树中某节点删除,还要保证树的结构不变,可以采用一种方法,那就是“虚节点”,也就是说,我们并不删除那个节点,却将需要删除的节点中保存的数据转移到一个新的节点中,这个节点独立于这棵树之外。这样一来,我们查询那个节点的时候,会查询到新的节点,而原本的节点的作用仅仅是保持树的结构。
因此,我们需要两个数组,一个数组是fm[N+M],一个数组是fle[N],其中fle[]数组的fle[i]表示第i个节点的值,而fm[]数组中,fm[fle[i]]用来保存第i个节点的前驱,即fle[i]的父节点。
重点就在于这个fle[],每次删除节点i时,我们不改变fm[fle[i]],这样树的结构就不会变,而我们赋予fle[i]一个从未使用过的新值,就使实际上的i节点变化了。
这样每次删除i节点,都只需要赋予fle[i]一个从未用过的新值,类似于给i节点换一个新马甲,就可以解决问题。
我们可以从值为n开始,给节点赋的值依次为n++,这样,因为最多M次操作,那么最多换M个马甲,因此,fm的大小为M+N。
上代码——
1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 #include <algorithm> 5 using namespace std; 6 7 const int N = 100010; 8 const int M = 1000010; 9 10 bool vis[N+M]; 11 int fm[N+M], fle[N]; 12 int n, m, a, b, pt, ans, tm; 13 char s[10]; 14 15 void init() 16 { 17 for(int i = 0; i < n; i++) 18 { 19 fm[i] = i; 20 fle[i] = i; 21 } 22 memset(vis, 0, sizeof(vis)); 23 pt = n; 24 ans = 0; 25 } 26 27 int mfind(int a) 28 { 29 int fa = a; 30 31 while(fa != fm[fa]) fa = fm[fa]; 32 while(a != fm[a]) 33 { 34 int mid = fm[a]; 35 fm[a] = fa; 36 a = mid; 37 } 38 return fa; 39 } 40 41 void mmerge() 42 { 43 int fa = mfind(fm[fle[a]]); 44 int fb = mfind(fm[fle[b]]); 45 if(fa != fb) fm[fa] = fb; 46 } 47 48 void dmerge() 49 { 50 fle[a] = pt; //换马甲 51 fm[pt] = pt; //给这个新值初始化父节点 52 pt++; //为新马甲做准备 53 } 54 55 void work() 56 { 57 while(m--) 58 { 59 scanf("%s", s); 60 if(s[0] == ‘M‘) 61 { 62 scanf("%d%d", &a, &b); 63 mmerge(); 64 } 65 else if(s[0] == ‘S‘) 66 { 67 scanf("%d", &a); 68 dmerge(); 69 } 70 } 71 72 for(int i = 0; i < n; i++) 73 { 74 int ance = mfind(fle[i]); 75 if(!vis[ance]) 76 { 77 ans++; 78 vis[ance] = 1; 79 } 80 } 81 printf("Case #%d: %d\n", tm++, ans); 82 } 83 84 85 86 int main() 87 { 88 //freopen("test.in", "r", stdin); 89 tm = 1; 90 while(~scanf("%d%d", &n, &m) && (n+m)) 91 { 92 init(); 93 work(); 94 } 95 }
ps: 这几天状态一直不好,但是今天突然看见一句话,感由心生,终于耐下心来弄明白了这道题——心若没有栖息的地方,在哪里都是流浪
继续加油吧……