并查集(二)
输入n对应n个样例
一共30000支舰队要求做两个命令
M 合 并 , C 查 询 M_{合并},C_{查询} M合并,C查询
查询的是两件队之间隔了多少其他舰队
不 知 道 合 并 的 道 理 是 什 么 为 什 么 是 m e r g e ( a , b ) 就 是 把 a 放 在 b 队 列 的 末 尾 , 但 是 还 是 随 缘 做 了 不知道合并的道理是什么为什么是merge(a,b)就是把a放在b队列的末尾,但是还是随缘做了 不知道合并的道理是什么为什么是merge(a,b)就是把a放在b队列的末尾,但是还是随缘做了
合并不输出消息,查询若两舰队再一个舰队中就输出他们的距离,反之不在一起就输出-1
用了些并查集的边权,其实就是每次模拟的时候多加一些属性
不像普通的并查集用fa[N]这里计入了三个数组维持更新信息
fa[] 指向父节点的数组
size[] 描述当前集合大小的数组
d[] 描述与根节点的距离
最后答案就是 a n s ( d [ a ] − d [ b ] ) − 1 ans(d[a] - d[b]) - 1 ans(d[a]−d[b])−1
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 30010;
int fa[N], d[N], size[N];
char F;
int n, A, B;
int get(int x){
if(fa[x] == x)
return x;
int root = fa[x];
//路径压缩
fa[x] = get(fa[x]);
//更新节点到根节点的距离
d[x] += d[root];
size[x] = size[fa[x]];
return fa[x];
}
void merge(int x, int y){
//查询父节点再连接
x = get(x);
y = get(y);
fa[x] = y; //x的父节点指向y
d[x] += (size[y] + d[y]);//x链接到y时前面就会多一个 y的舰队的大小
size[x] += size[y]; //y的大小也要改变
size[y] = size[x];
}
int main(){
// 预处理 fa 为指向根节点的数组初始化为自己
// 预处理 d 为描述该队列向根节点连线的边权初始化为0
// 预处理 size 为描述每个队列的大小(初始化为1)以便于连接时计算前方边权的累加
for(int i = 1; i <= 30000; i++)
{
fa[i] = i;
d[i] = 0;
size[i] = 1;
}
cin >> n;
while(n--){
cin >> F >> A >> B;
if(F == 'M'){ //M代表合并的标志
//如果在一条队列上不进行合并
if(get(A) != get(B))
merge(A, B);
}else if(F == 'C'){ //查询
//如果不在一个队列输出-1,反之输出中间隔了多少个战舰
// -1 / abs(d(A) - d(B)) - 1
if(get(A) == get(B))
cout << abs(d[A] - d[B]) - 1 << endl;
else
cout << -1 << endl;
}
}
return 0;
}