hdu 2473 Junk-Mail Filter (暴力并查集)
为什么标题写的是暴力并查集?因为我的解法跟网上的有所不同,方法暴力很多。
先解释题意,这是一个模拟处理垃圾邮件的问题。垃圾邮件要根据它们的性质进行分类。对于10w个邮件,操作M是这两个邮件具有相同的属性,属性是可以传递的,也就是说,1和2有相同属性,2和3有相同属性,那么1和3就有相同属性。操作S是将这个邮件分离出来,不和任何的邮件有相同属性。最后问不同属性的邮件有多少种。这么看来,这是一个并查集。网上的正解,是把那个删除的结点当成虚拟结点,继续放在那里。然后重新开一个新的结点来存放分离出来的结点。
而我的做法则是直接构造并查集树,我的树是会记录子结点有那些的,更新的时候就只能直接暴力更新了。
代码如下:
1 #include <iostream> 2 #include <algorithm> 3 #include <set> 4 #include <cstring> 5 #include <cstdio> 6 #include <stack> 7 8 using namespace std; 9 10 const int N = 111111; 11 stack<int> tmp; 12 13 struct MFS { 14 set<int> sn[N]; 15 int fa[N]; 16 void init() { for (int i = 0; i < N; i++) fa[i] = i, sn[i].clear(), sn[i].insert(i);} 17 int find(int x) { 18 while (!tmp.empty()) tmp.pop(); 19 while (fa[x] != x) { 20 tmp.push(x); 21 sn[fa[x]].erase(x); 22 x = fa[x]; 23 } 24 while (!tmp.empty()) { 25 fa[tmp.top()] = x; 26 sn[x].insert(tmp.top()); 27 tmp.pop(); 28 } 29 return x; 30 } 31 void merge(int x, int y) { 32 int fx = find(x), fy = find(y); 33 if (fx == fy) return ; 34 sn[fa[fy]].erase(fy); 35 fa[fy] = fx; 36 sn[fx].insert(fy); 37 } 38 void reset(int x) { 39 set<int>::iterator si = sn[x].begin(); 40 if (fa[x] == x) { 41 if (*si == x) si++; 42 if (si == sn[x].end()) return ; 43 int t = *si; 44 fa[t] = t; 45 sn[t].insert(t); 46 si++; 47 while (si != sn[x].end()) { 48 if (*si == x) { si++; continue;} 49 fa[*si] = t; 50 sn[t].insert(*si); 51 si++; 52 } 53 } else { 54 int fx = find(x); 55 sn[fx].erase(x); 56 while (si != sn[x].end()) { 57 fa[*si] = fx; 58 sn[fx].insert(*si); 59 si++; 60 } 61 } 62 fa[x] = x; 63 sn[x].clear(); 64 sn[x].insert(x); 65 } 66 } mfs; 67 bool vis[N]; 68 69 int main() { 70 // freopen("in", "r", stdin); 71 int n, m, x, y, cas = 1; 72 char op[3]; 73 while (~scanf("%d%d", &n, &m) && (n || m)) { 74 mfs.init(); 75 for (int i = 0; i < m; i++) { 76 scanf("%s", op); 77 if (op[0] == 'M') { 78 scanf("%d%d", &x, &y); 79 //mfs.reset(y); 80 mfs.merge(x, y); 81 } else { 82 scanf("%d", &x); 83 mfs.reset(x); 84 } 85 // for (int i = 0; i < 3; i++) cout << mfs.fa[i] << ' '; cout << endl; 86 } 87 memset(vis, 0, sizeof(vis)); 88 int cnt = 0; 89 for (int i = 0, t; i < n; i++) { 90 t = mfs.find(i); 91 if (vis[t]) continue; 92 cnt++; 93 vis[t] = true; 94 } 95 printf("Case #%d: %d\n", cas++, cnt); 96 } 97 return 0; 98 } 99 100 /* 101 5 6 102 M 0 1 103 M 1 2 104 M 1 3 105 S 1 106 M 1 2 107 S 3 108 109 3 4 110 M 1 2 111 M 0 1 112 S 1 113 S 0 114 115 0 0 116 */
用虚拟结点的方法将会尽快更新。
——written by Lyon