hdu 2818 Building Block(加权并查集)2009 Multi-University Training Contest 1
题意:
一共有30000个箱子,刚开始时都是分开放置的。接下来会有两种操作:
1. M x y,表示把x箱子所在的一摞放到y箱子那一摞上。
2. C y,表示询问y下方有多少个箱子。
输入:
首行输入一个整数m,表示一共有m次操作。
接下来每次操作都是上面所说的两种中的一种。
输出:
每次C y,输出一个结果,每次输出占1行。
就是不知道究竟是只有一组测试样例还是有多组测试样例,不过我还是按照多组的方式写了。
题解:
这道题我自己思考了好久都没有思路,最后中忍不住去看了题解,然后被题解震惊了。
明明只有1个权值,题解中的方式愣是把这一个权拆成了两部分,然后算出结果,Orz。
两个权是tot[], sum[],tot[i]表示i所在的箱子堆中一共有tot[i]个箱子,其中只有根节点的tot[i]是必须的。sum[i]则表示i下方共有sum[i]个箱子。
接下来,每次合并时,都将被合并的根节点fx的sum[]值加上仍然存在的根节点fy的tot[]值,然后fy的tot[]则加上fx的tot[]。
即,sum[fx] += tot[fy]; tot[fy] += tot[fx];
接下来,每次查找时,都要讲节点x的sum[]加上x的父节点的sum[]。
即,sum[x] += sum[fm[x]];
具体见代码——
1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 #include <algorithm> 5 using namespace std; 6 7 const int N = 30010; 8 9 int fm[N], sum[N], tot[N]; 10 int n; 11 int a, b; 12 char s[2]; 13 14 void init() 15 { 16 for(int i = 0; i < N; i++) //很坑爹,题目明明说是1~30000,但是必须将0也初始化才能过 17 { 18 fm[i] = i; 19 sum[i] = 0; 20 tot[i] = 1; 21 } 22 } 23 24 int mfind(int x) 25 { 26 if(x == fm[x]) return x; 27 int t = fm[x]; 28 fm[x] = mfind(fm[x]); 29 sum[x] += sum[t]; 30 return fm[x]; 31 } 32 33 void mmerge() 34 { 35 int fx = mfind(a); 36 int fy = mfind(b); 37 if(fx != fy) 38 { 39 fm[fx] = fy; 40 sum[fx] += tot[fy]; 41 tot[fy] += tot[fx]; 42 } 43 } 44 45 void work() 46 { 47 for(int tm = 1; tm <= n; tm++) 48 { 49 scanf("%s", s); 50 if(s[0] == 'M') 51 { 52 scanf("%d%d", &a, &b); 53 mmerge(); 54 } 55 else 56 { 57 scanf("%d", &a); 58 mfind(a); 59 printf("%d\n", sum[a]); 60 } 61 } 62 } 63 64 int main() 65 { 66 //freopen("test.in", "r", stdin); 67 while(~scanf("%d", &n)) 68 { 69 init(); 70 work(); 71 } 72 return 0; 73 }