238. 银河英雄传说 - AcWing题库

有一个划分为 列的星际战场,各列依次编号为

艘战舰,也依次编号为 ,其中第 号战舰处于第 列。

条指令,每条指令格式为以下两种之一:

  1. M i j,表示让第 号战舰所在列的全部战舰保持原有顺序,接在第 号战舰所在列的尾部。
  2. 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;
}