洛谷传送门
题目描述
永无乡包含 n n 座岛,编号从 到 n n ,每座岛都有自己的独一无二的重要度,按照重要度可以将这 座岛排名,名次用 1 1 到 来表示。某些岛之间由巨大的桥连接,通过桥可以从一个岛到达另一个岛。如果从岛 a a 出发经过若干座(含 座)桥可以 到达岛 b b ,则称岛 和岛 b b 是连通的。
现在有两种操作:
表示在岛 x x 与岛 之间修建一座新桥。
Qxk Q x k 表示询问当前与岛 x x 连通的所有岛中第 重要的是哪座岛,即所有与岛 x x 连通的岛中重要度排名第 小的岛是哪座,请你输出那个岛的编号。
输入输出格式
输入格式:
第一行是用空格隔开的两个正整数 n n 和 ,分别表示岛的个数以及一开始存在的桥数。
接下来的一行是用空格隔开的 n n 个数,依次描述从岛 到岛 n n 的重要度排名。随后的 mm 行每行是用空格隔开的两个正整数 和 bi b i ,表示一开始就存在一座连接岛 ai a i 和岛 bi b i 的桥。
后面剩下的部分描述操作,该部分的第一行是一个正整数 q q ,表示一共有 个操作,接下来的 q q 行依次描述每个操作,操作的 格式如上所述,以大写字母 或 B B 开始,后面跟两个不超过 的正整数,字母与数字以及两个数字之间用空格隔开。
输出格式:
对于每个 Qxk Q x k 操作都要依次输出一行,其中包含一个整数,表示所询问岛屿的编号。如果该岛屿不存在,则输出 −1 − 1 。
输入输出样例
输入样例#1:
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
输出样例#1:
-1
2
5
1
2
说明
对于 20% 的数据 n≤1000,q≤1000 n ≤ 1000 , q ≤ 1000
对于 100% 的数据 n≤100000,m≤n,q≤300000 n ≤ 100000 , m ≤ n , q ≤ 300000
解题分析
显然我们需要动态维护并查集, 并且查询并查集内第 k k <script type="math/tex" id="MathJax-Element-39">k</script>大。 很显然这样的操作主席树可以胜任。 但我们需要合并两个并查集内的信息, 自然需要合并两棵主席树。
和普通的主席树不一样的是, 我们不需要在前一棵树的基础上建立下一棵主席树, 而只需要建出每个小岛代表的一棵树。 如果我们每次将小的树合并到大的树上去的话, 因为最多合并n次, 所以复杂度是科学的。
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdlib>
#define R register
#define IN inline
#define gc getchar()
#define W while
#define ls tree[now].son[0]
#define rs tree[now].son[1]
#define MX 100005
template <class T>
IN void in(T &x)
{
x = 0;R char c = gc;
W (!isdigit(c)) c = gc;
W (isdigit(c))
x = (x << 1) + (x << 3) + c - 48, c = gc;
}
namespace President_Tree
{
struct Node
{
int son[2], sum;
}tree[MX << 5];
int fa[MX], arr, dot, line, q, rk[MX], dat[MX], root[MX];
int find(const int &now){return fa[now] == now ? now : fa[now] = find(fa[now]);}//并查集操作
void insert(int &now, const int &lef, const int &rig, const int &pos)//建立一棵新树
{
if(!now) now = ++arr;
tree[now].sum++;
if(lef == rig) return;
int mid = lef + rig >> 1;
if(mid >= pos) insert(ls, lef, mid, pos);
else insert(rs, mid + 1, rig, pos);
}
void merge(int &nw, int pr)
{// 合并两棵线段树, 其中nw表示目标树, pr表示向nw合并的一棵树。
if(!nw) return nw = pr, void();
if(!pr) return;
tree[nw].sum += tree[pr].sum;
merge(tree[nw].son[0], tree[pr].son[0]);
merge(tree[nw].son[1], tree[pr].son[1]);
}
int query(const int &now, const int &lef, const int &rig, const int &tar)
//查询树上第k大
{
if(lef == rig) return rk[lef];
int lf = tree[ls].sum; int mid = lef + rig >> 1;
if(lf >= tar) return query(ls, lef, mid, tar);
else return query(rs, mid + 1, rig, tar - lf);
}
}
using namespace President_Tree;
int main(void)
{
int a, b, x, y;
char buf[5];
in(dot);in(line);
for (R int i = 1; i <= dot; ++i) fa[i] = i;
for (R int i = 1; i <= dot; ++i)
{
in(dat[i]); rk[dat[i]] = i;
insert(root[i], 1, dot, dat[i]);
}
for (R int i = 1; i <= line; ++i)
{
in(a), in(b);
x = find(a), y = find(b);
if(tree[root[x]].sum > tree[root[y]].sum)//启发式合并
merge(root[x], root[y]), fa[y] = x;
else merge(root[y], root[x]), fa[x] = y;
}
in(q);
W (q--)
{
scanf("%s", buf); in(a), in(b);
switch (buf[0])
{
case 'Q':
{
if(tree[root[find(a)]].sum < b) printf("-1\n");
else printf("%d\n", query(root[find(a)], 1, dot, b));
break;
}
case 'B':
{
x = find(a), y = find(b);
if(tree[root[x]].sum > tree[root[y]].sum)
merge(root[x], root[y]), fa[y] = x;
else merge(root[y], root[x]), fa[x] = y;
}
}
}
}