杭电3635——(带权)并差集
写在题前:
这个题用的并差集需要带权值,刚开始不是那么容易理解,不过你转过那个弯就很简单了。
解题步骤:
1、转移龙珠的时候:
- 先找到当前龙珠的位置,再将这个位置的所有龙珠放在另一个位置。
2、查询的时候:
- 直接访问数组就行。
写在题后:
我本来以为输入数据可能会出现环的情况,好不容易写完了,结果wa了,后来才发现输入的数据根本不会出现有环的情况,坑死人!!!
ac代码:
# include <iostream>
# include <algorithm>
# include <cstdio>
# include <cstdlib>
# include <string>
# include <cstring>
# include <vector>
using namespace std;
const int maxn = 10010;
int id[maxn];//id[x]:龙珠x所在的城市
int num[maxn];//num[x]:城市x龙珠的数量
int cnt[maxn];//cnt[x]:龙珠x移动的次数
int fin(int x) {//找到龙珠x所在的城市,需要用路径压缩
/*
fin函数流程:
我们可以将fin函数看做两个部分,一个是路径压缩,一个是cnt数组的更新,
cnt的更新是在寻找最上面节点的过程中更新的,
流程如下:
如果id[x]等于x,说明龙珠x在城市x这里,直接返回;
如果不等,用t记录id[x]的值,用于更新cnt数组
令id[x]=fin(id[x]),(用了递归);
然后让cnt[x]在本来的基础上加上cnt[t]的值;
返回id[x]的值。
cnt更新的具体流程(就是递归的一个过程):
举个例子:1的上面是2,2的上面是3,3的上面是4,4就是最上面的节点。
需要我们更新4个节点的cnt值,正常的顺序应该是4->3->2->1,从上往下更新的
但是我们没办法从上往下遍历,只能从下往上遍历,因此我们可以这样做;
我们从1开始,
发现1上面有2,找2,{
发现2上面有3,找3,{
发现3上面有4,找4,{
发现4上面没有节点,直接返回
}
将cnt[3]在自己的基础上加上cnt[4]的值,
}
将cnt[2]在自己的基础上加上cnt[3]的值,
}
将cnt[1]在自己的基础上加上cnt[2]的值,
*/
if(id[x] == x) {
return id[x];
}
int t = id[x];
id[x] = fin(id[x]);
cnt[x] += cnt[t];
return id[x];
}
void meg(int x, int y) {
int a = fin(x);
int b = fin(y);//找到龙珠x所在的城市a,找到龙珠y所在的城市b
if (a !=b) {
id[a] = b;//将城市a父亲是城市b,连接的过程。
}
num[b] += num[a];//城市a的龙珠全给城市a
num[a] = 0;//城市a的龙珠清零。
cnt[a] ++;//龙珠a移动次数加一
}
bool same(int x, int y) {
return fin(x) == fin(y);
}
int main() {
ios::sync_with_stdio(false);
int T;
cin >> T;
for (int ti = 1; ti <= T; ti++) {
cout << "Case " << ti << ":" << endl;
for (int i = 0; i < maxn; i++) {
id[i] = i;
num[i] = 1;
}
memset(cnt,0,sizeof (cnt));
//注意清空数据。
int m, n;
cin >> n >> m;
while(m--) {
string str;
cin >> str;
if (str[0] == 'T') {
int x, y;
cin >> x >> y;
meg(x, y);
}
else if (str[0] == 'Q') {
int t;
cin >> t;
int x = fin(t);
cout << x << " " << num[x] << " " << cnt[t] << endl;
}
}
}
return 0;
}