题目地址:
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
op 为
Q
,则表示询问所有与岛 x x x 连通的岛中重要度排名第 y y y 小的岛是哪座,请你输出那个岛的编号。 - 若
o
p
op
op 为
B
,则表示在岛 x x x 与岛 y y y 之间修建一座新桥。
输出格式:
对于每个询问操作都要依次输出一行一个整数,表示所询问岛屿的编号。如果该岛屿不存在,则输出
−
1
-1
−1 。
数据范围:
对于
20
%
20\%
20% 的数据,保证
n
≤
1
0
3
n \leq 10^3
n≤103,
q
≤
1
0
3
q \leq 10^3
q≤103。
对于
100
%
100\%
100% 的数据,保证
1
≤
m
≤
n
≤
1
0
5
1 \leq m \leq n \leq 10^5
1≤m≤n≤105,
1
≤
q
≤
3
×
1
0
5
1 \leq q \leq 3 \times 10^5
1≤q≤3×105,
p
i
p_i
pi 为一个
1
∼
n
1 \sim n
1∼n 的排列,
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
1≤u,v,x,y≤n。
思路是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)。