题意:共有30000个人,编号为1-30000,开始时每个人一列。然后进行两种操作:M i j表示把第i个人所在的列的队首加到第j个人所在的列的队尾。C i j查询第i个人和第j个人是否在一列,假如在一列询问i和j间隔着几个人。
题解:
1.不同于以前的带权并查集,这个除了fa数组和d数组外,加了一个num数组,num[i]表示第i列为队首的队列有几个人。d[i]表示i到队首有几个人。
#include<bits/stdc++.h>
using namespace std ;
#define N 30005
typedef long long ll ;
int t ;
int fa[N] ;
int d[N] ;//d[i]表示第i个人到队首有几个人。
int num[N] ;//num[i]表示以第i个人为队首有几个人。
void init()
{
int i ;
for(i = 1 ; i <= N - 5 ; i ++)
{
fa[i] = i ;
d[i] = 0 ;
num[i] = 1 ;
}
}
int get(int x)
{
int temp ;
if(fa[x] == x)
return x ;
temp = fa[x] ;
fa[x] = get(fa[x]) ;
d[x] += d[temp] ; //边权相加,因为路径压缩,所以只会累加一次,不用担心多次累加。
return fa[x] ;
}
void merge(int x , int y)
{
int a = get(x) , b = get(y) ;
d[a] = num[b] ;
num[b] += num[a] ; //两个队列连接时进行num数组的累加。
fa[a] = b ;
}
void query(int x , int y)
{
if(get(x) != get(y))
{
printf("-1\n") ;
return ;
}
printf("%d\n" , abs(d[x] - d[y]) - 1) ;
}
int main()
{
int i , j ;
char s[2] ;
int x , y ;
scanf("%d" , &t) ;
init() ;
while(t --)
{
scanf("%s%d%d" , s , &x , &y) ;
if(s[0] == 'M')
merge(x , y) ;
else
query(x , y) ;
}
}