有一个划分为 列的星际战场,各列依次编号为 。
有 艘战舰,也依次编号为 ,其中第 号战舰处于第 列。
有 条指令,每条指令格式为以下两种之一:
M i j
,表示让第 号战舰所在列的全部战舰保持原有顺序,接在第 号战舰所在列的尾部。C i j
,表示询问第 号战舰与第 号战舰当前是否处于同一列中,如果在同一列中,它们之间间隔了多少艘战舰。
现在需要你编写一个程序,处理一系列的指令。
输入格式
第一行包含整数 ,表示共有 条指令。
接下来 行,每行一个指令,指令有两种形式:M i j
或 C i j
。
其中 和 为大写字母表示指令类型, 和 为整数,表示指令涉及的战舰编号。
输出格式
你的程序应当依次对输入的每一条指令进行分析和处理:
如果是 M i j
形式,则表示舰队排列发生了变化,你的程序要注意到这一点,但是不要输出任何信息;
如果是 C i j
形式,你的程序要输出一行,仅包含一个整数,表示在同一列上,第 号战舰与第 号战舰之间布置的战舰数目,如果第 号战舰与第 号战舰当前不在同一列上,则输出 。
数据范围
输入样例:
4
M 2 3
C 1 2
M 2 4
C 4 2
输出样例:
-1
1
思路
若只有求是否在一列中的操作时, 该题就是简单的并查集问题。求距离时, 定义 d[i]
为 i
点到 p[i]
的距离, s[i]
为 i
所在集合的点数量。
这样对于求同一列的 a,b 两点距离时, 就可以用 abs(d[b] - d[a])
求得, 前缀和思想。
当 a 接到 b 后面时, d[pa] = s[pb]
将 a 集合头节点的祖先距离设置为到 pb
的距离, 此时因为 a 集合内的点头节点都是 pa, 故当进行 find 操作时, 会把 d[pa]
加到 d[a]
上, 从而实现更新 a 节点的祖先距离。
同时也要把 s[pb] += s[pa]
因为集合合并后, 点数量增加。
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
using namespace std;
const int N = 3e4 + 10;
int p[N], dist[N], s[N];
int n;
int find(int x)
{
if(x != p[x])
{
int root = find(p[x]);
dist[x] += dist[p[x]];
p[x] = root;
}
return p[x];
}
void merge(int a, int b)
{
int pa = find(a), pb = find(b);
if(pa == pb) return;
p[pa] = pb;
dist[pa] = s[pb];
s[pb] += s[pa];
}
int main()
{
cin >> n;
for(int i = 0; i < N; i++)
p[i] = i, s[i] = 1;
while(n--)
{
char c[2];
int a, b;
cin >> c >> a >> b;
if(c[0] == 'M')
merge(a,b);
else
{
int pa = find(a), pb = find(b);
if(pa != pb) cout << -1 << endl;
else cout << max(abs(dist[a] - dist[b]) - 1, 0) << endl;
}
}
return 0;
}