题意:每次把一个序列接到另一个序列的末尾,求某次操作之后两元素的距离。
题解:集合问题一般采用并查集。注意到每个集合必定是一条链,于是对每个集合,维护链头(即集合的根节点),链尾,每个节点到其父节点的距离。查询操作同带权并查集,合并操作就把一条链的链头接到另一条链的链尾。
需要注意的是,路径压缩的过程中,先记录该节点原来的父节点,递归完成后再用父节点的dis值更新该节点的dis值。否则会用根节点的dis值(0)更新该节点,造成错误。
看了其他的题解发现可以直接记录该集合的size值来代替链尾的记录
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 30005;
int fa[MAXN], dis[MAXN], tail[MAXN];
int get(int t){
if(fa[t] != t){
int f = fa[t];
fa[t] = get(fa[t]);
dis[t] += dis[f];
// tail[fa[t]] = t;
}
return fa[t];
}
void merge(int a, int b){
int A = get(a), B = get(b);
if(A != B){
fa[A] = tail[B];
dis[A] = 1;
tail[B] = tail[A];
}
}
int found(int a, int b){
int A = get(a), B = get(b);
if(A != B) return -1;
int ret = dis[a] - dis[b];
if(ret < 0) ret = -ret;
ret -= 1;
return ret;
}
int main(){
// freopen("in.txt", "r", stdin);
for(int i = 1; i <= 30000; i++) fa[i] = i, tail[i] = i;
int T;
scanf("%d", &T);
for(int i = 1; i <= T; i++){
char ch; int a, b;
scanf("
%c%d%d", &ch, &a, &b);
if(ch == 'M'){
merge(a, b);
}
else{
printf("%d
", found(a, b));
}
}
return 0;
}