[NOI2002]银河英雄传说

有一个划分为N列的星际战场, 各列依次编号为 1, 2, ···, N. 有 N 艘战舰, 也依次编号为 1 - N, 初始时, 第 i 号战舰在第 i 列.

T条指令, 有两种格式.

1. M i j, 表示让第 i 号战舰所在列的全部战舰保持原有的顺序, 接在第 j 号战舰所在列的尾部.

2. C i j, 表示询问第 i 号战舰与第 j 号战舰是否处在同一列中, 如果在同一列中, 他们之间间隔了多少艘战舰.

N <= 30000, T <= 5e5.

 

这道题很像是银行窗口排队, 要用到边带权的并查集, 我最开始看到这道题的时候, 当时不是很理解find的递归...后来就好点了.

其实我感觉迭代版的更好理解.

我们要用并查集维护一个链, 选出链头作为代表元.

size[i] 代表  i这条链上的节点的数量, 只在 i 是代表元时才有意义;

faz[i] 代表 i 的 父亲节点;

data[i] 代表 i 到 faz[i] 的边的数量, 即距离.

因为我们要进行路径压缩, 所以find函数和merge函数要与平常的做些修改.

#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
const int MAXN = 30600;
using namespace std;
int size[MAXN], faz[MAXN], data[MAXN];
void init(){
    for(int i = 0; i < MAXN; ++i){
        size[i] = 1;// 只有根节点的 size 才有意义
        data[i] = 0;
        faz[i] = i;
    }
}
int find(int x){// 递归
    if(x == faz[x]){
        return x;
    }
    int root = find(faz[x]);
    data[x] += data[faz[x]];
    return faz[x] = root;
}
int find(int x){// 迭代
    int root = x, sum = 0;
    while(root != faz[root]){
        sum += data[root];
        root = faz[root];
    }
    int father, weight;
    while(x != root){
        father = faz[x];
        weight = data[x];
        data[x] = sum;
        faz[x] = root;
        sum -= weight;

        x = father;
    }
    return root;
}
void merge(int x, int y){
    x = find(x), y = find(y);
    faz[x] = y;
    data[x] = size[y];
    size[y] += size[x];
}
int main(){
    init();
    int t;
    scanf("%d", &t);
    int x, y;
    char cmd;
    while(t--){
        getchar();
        scanf("%c%d%d", &cmd, &x, &y);
        if(cmd == 'M'){
            merge(x, y);
        }
        else{
            if(find(x) == find(y)){
                printf("%d\n", abs(data[x] - data[y]) - 1);
            }
            else{
                printf("-1\n");
            }
        }
    }
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值