【题目描述】
永无乡包含 n 座岛,编号从 1 到 n ,每座岛都有自己的独一无二的重要度,按照重要度可以将这 n 座岛排名,名次用 1 到 n 来表示。某些岛之间由巨大的桥连接,通过桥可以从一个岛到达另一个岛。如果从岛 a 出发经过若干座(含 0 座)桥可以 到达岛 b ,则称岛 a 和岛 b 是连通的。
现在有两种操作:
B x y 表示在岛 x 与岛 y 之间修建一座新桥。
Q x k 表示询问当前与岛 x 连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k 小的岛是哪座,请你输出那个岛的编号。
【输入格式】
第一行是用空格隔开的两个正整数 n 和 m ,分别表示岛的个数以及一开始存在的桥数。
接下来的一行是用空格隔开的 n 个数,依次描述从岛 1 到岛 n 的重要度排名。随后的 m 行每行是用空格隔开的两个正整数 ai 和 bi,表示一开始就存在一座连接岛 ai 和岛 bi 的桥。
后面剩下的部分描述操作,该部分的第一行是一个正整数 q,表示一共有 q 个操作,接下来的 q 行依次描述每个操作,操作的 格式如上所述,以大写字母 Q 或 B 开始,后面跟两个不超过 n 的正整数,字母与数字以及两个数字之间用空格隔开。
【输出格式】
对于每个 Q x k 操作都要依次输出一行,其中包含一个整数,表示所询问岛屿的编号。如果该岛屿不存在,则输出 -1 。
S a m p l e I n p u t Sample~~Input Sample Input
5 1
4 3 2 5 1
1 2
7
Q 3 2
Q 2 1
B 2 3
B 1 5
Q 2 1
Q 2 4
Q 2 3
S a m p l e O u t p u t Sample~~Output Sample Output
-1
2
5
1
2
【题意分析】
跟yyborz学的splaydsu
题意就是维护一些联通块,并查询在联通块中的排名。
有合并操作:将两个联通块合并。
这个dsu很暴力:启发式将小树节点一个一个插到大树里进去。因为size[]
可以预处理。
还有预处理比较麻烦:我们构建N棵splay,第i棵的根节点位于N+i,这样我们就建立起了对应关系。然后splay的时候如果发现目标节点<=N,那么就意味着旋到了某棵splay的根,然后将这棵splay的根重置。
注意前1至N个节点是虚拟的,真正的节点从N+1开始,从N+1开始只是方便处理根节点。
所以之后插入节点要从2N+1开始。
Code:
#include
#include
#include
#include
#include
#include
#define MAXN 300200
using namespace std;
int n, m, sz, id[MAXN];
struct _splay {
int size[MAXN], ff[MAXN], father[MAXN], son[MAXN][2], val[MAXN], root[MAXN];
inline void maintain (int x) {
size[x] = size[son[x][0]] + size[son[x][1]] + 1;
}
int getfather (int x) {
return (x == ff[x]) ? x : ff[x] = getfather (ff[x]);
}
inline void rotate (int x) {
int y = father[x], z = father[y];
int k = son[y][1] == x, kk = son[z][1] == y;
son[z][kk] = x;
father[x] = z;
son[y][k] = son[x][k ^ 1];
father[son[x][k ^ 1]] = y;
son[x][k ^ 1] = y;
father[y] = x;
maintain (y), maintain (x);
}
inline void splay (int x, int goal) {
while (father[x] != goal) {
int y = father[x], z = father[y];
if (z != goal)
(son[y][1] == x) ^ (son[z][1] == y)
? rotate (x) : rotate (y);
rotate (x);
}
if (goal <= n) root[goal] = x;
}
inline void insert (int x, int y) {
int now = root[y], fa = y;
while (now && x != val[now]) fa = now, now = son[now][x > val[now]];
now = ++sz;
if (fa > n) son[fa][x > val[fa]] = now;
son[now][0] = son[now][1] = 0,
size[now] = 1, father[now] = fa,
val[now] = x, splay (now, y);
}
void DFS (int now, int y) {
if (son[now][0]) DFS (son[now][0], y);
insert (val[now], y);
if (son[now][1]) DFS (son[now][1], y);
}
inline void merge (int xx, int yy) {
int x = getfather (xx), y = getfather (yy);
if (x == y) return;
if (size[root[x]] > size[root[y]]) swap (x, y);
ff[x] = y; DFS (root[x], y);
}
int Kth (int x, int rank) {
if (rank > size[x]) return -1;
if (son[x][0] && rank <= size[son[x][0]]) return Kth (son[x][0], rank);
return (rank <= size[son[x][0]] + 1)
? val[x] : Kth (son[x][1], rank - size[son[x][0]] - 1);
}
}tree;
int main () {
scanf ("%d%d", &n, &m), sz = n * 2;
for (register int i = 1; i <= n; i++) {
int x; scanf ("%d", &x);
tree.root[i] = n + i, tree.ff[i] = tree.father[n + i] = i;
tree.val[n + i] = x, tree.size[n + i] = 1, id[x] = i;
}
for (register int i = 1; i <= m; i++) {
int x, y; scanf ("%d%d", &x, &y);
tree.merge (x, y);
}
scanf ("%d", &m);
for (register int i = 1; i <= m; i++) {
char opt[5]; int x, y;
scanf ("%s", opt), scanf ("%d%d", &x, &y);
if (opt[0] == 'B') tree.merge (x, y);
if (opt[0] == 'Q') {
int z = tree.Kth (tree.root[tree.getfather (x)], y);
if (z == -1) puts ("-1");
else printf ("%d\n", id[z]);
}
}
return 0;
}