【洛谷】P3224 永无乡

题目地址:

https://www.luogu.com.cn/problem/P3224

题目描述:
永无乡包含 n n n 座岛,编号从 1 1 1 n n n ,每座岛都有自己的独一无二的重要度,按照重要度可以将这 n n n 座岛排名,名次用 1 1 1 n n n 来表示。某些岛之间由巨大的桥连接,通过桥可以从一个岛到达另一个岛。如果从岛 a a a 出发经过若干座(含 0 0 0 座)桥可以 到达岛 b b b ,则称岛 a a a 和岛 b b b 是连通的。
现在有两种操作:
B x y 表示在岛 x x x 与岛 y y y 之间修建一座新桥。
Q x k 表示询问当前与岛 x x x 连通的所有岛中第 k k k 重要的是哪座岛,即所有与岛 x x x 连通的岛中重要度排名第 k k k 小的岛是哪座,请你输出那个岛的编号。

输入格式:
第一行是用空格隔开的两个整数,分别表示岛的个数 n n n 以及一开始存在的桥数 m m m
第二行有 n n n 个整数,第 i i i 个整数表示编号为 i i i 的岛屿的排名 p i p_i pi
接下来 m m m 行,每行两个整数 u , v u, v u,v,表示一开始存在一座连接编号为 u u u 的岛屿和编号为 v v v 的岛屿的桥。
接下来一行有一个整数,表示操作个数 q q q
接下来 q q q 行,每行描述一个操作。每行首先有一个字符 o p op op,表示操作类型,然后有两个整数 x , y x, y x,y

  • o p op opQ,则表示询问所有与岛 x x x 连通的岛中重要度排名第 y y y 小的岛是哪座,请你输出那个岛的编号。
  • o p op opB,则表示在岛 x x x 与岛 y y y 之间修建一座新桥。

输出格式:
对于每个询问操作都要依次输出一行一个整数,表示所询问岛屿的编号。如果该岛屿不存在,则输出 − 1 -1 1

数据范围:
对于 20 % 20\% 20% 的数据,保证 n ≤ 1 0 3 n \leq 10^3 n103, q ≤ 1 0 3 q \leq 10^3 q103
对于 100 % 100\% 100% 的数据,保证 1 ≤ m ≤ n ≤ 1 0 5 1 \leq m \leq n \leq 10^5 1mn105, 1 ≤ q ≤ 3 × 1 0 5 1 \leq q \leq 3 \times 10^5 1q3×105 p i p_i pi 为一个 1 ∼ n 1 \sim n 1n 的排列, o p ∈ { Q , B } op \in \{\texttt Q, \texttt B\} op{Q,B} 1 ≤ u , v , x , y ≤ n 1 \leq u, v, x, y \leq n 1u,v,x,yn

思路是FHQ Treap。参考https://blog.csdn.net/qq_46105170/article/details/121529087。代码如下:

#include <cstdio>
#include <iostream>
#include <string>
using namespace std;

namespace {

const int N = 1e5 + 10;
int n, m;
struct Node {
#define lc(p) tr[p].l
#define rc(p) tr[p].r
#define val(p) tr[p].val
#define id(p) tr[p].id
#define sz(p) tr[p].sz
  int l, r;
  int id, val;
  int sz, rnd;
} tr[N];
int idx;
int root[N], p[N];

int find(int x) {
  if (x == p[x]) return x;
  return p[x] = find(p[x]);
}

int get_node(int id, int val) {
  tr[++idx] = {0, 0, id, val, 1, rand()};
  return idx;
}

#define pushup(p) sz(p) = sz(lc(p)) + sz(rc(p)) + 1

int merge(int x, int y) {
  if (!x || !y) return x | y;
  if (tr[x].rnd > tr[y].rnd) {
    rc(x) = merge(rc(x), y);
    pushup(x);
    return x;
  } else {
    lc(y) = merge(x, lc(y));
    pushup(y);
    return y;
  }
}

void split(int p, int val, int &x, int &y) {
  if (!p) x = y = 0;
  else {
    if (val < val(p)) {
      y = p;
      split(lc(p), val, x, lc(p));
    } else {
      x = p;
      split(rc(p), val, rc(p), y);
    }
    pushup(p);
  }
}

int get_id(int id, int rk) {
  int p = root[id];
  while (p) {
    if (rk <= sz(lc(p))) p = lc(p);
    else if (rk > sz(lc(p)) + 1) rk -= sz(lc(p)) + 1, p = rc(p);
    else return id(p);
  }
  return -1;
}

void insert(int u, int b) {
  int x, y;
  split(root[b], val(u), x, y);
  root[b] = merge(merge(x, u), y);
}

void dfs(int u, int b) {
  if (!u) return;
  dfs(lc(u), b);
  dfs(rc(u), b);
  insert(u, b);
}

}  // namespace

int main() {
  scanf("%d%d", &n, &m);
  for (int i = 1, x; i <= n; i++) {
    scanf("%d", &x);
    p[i] = i;
    root[i] = get_node(i, x);
  }

  while (m--) {
    int x, y;
    scanf("%d%d", &x, &y);
    x = find(x), y = find(y);
    if (x != y) {
      if (sz(root[x]) > sz(root[y])) swap(x, y);
      p[x] = y;
      dfs(root[x], y);
    }
  }

  scanf("%d", &m);
  while (m--) {
    int x, y;
    char op[2];
    scanf("%s%d%d", op, &x, &y);
    x = find(x);
    if (*op == 'B') {
      y = find(y);
      if (x != y) {
        if (sz(root[x]) > sz(root[y])) swap(x, y);
        p[x] = y;
        dfs(root[x], y); 
      }
    } else {
      int rk = y;
      if (sz(root[x]) < rk) puts("-1");
      else printf("%d\n", get_id(x, rk));
    }
  }
}

时间复杂度 O ( n log ⁡ 2 n + m log ⁡ n ) O(n\log^2n+m\log n) O(nlog2n+mlogn),空间 O ( n ) O(n) O(n)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值