题目地址:

https://www.acwing.com/problem/content/description/1065/

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

输入格式:
第一行是用空格隔开的两个正整数nnn和mmm,分别表示岛的个数以及一开始存在的桥数。接下来的一行是用空格隔开的nnn个数,依次描述从岛111到岛nnn的重要度排名。随后的mmm行每行是用空格隔开的两个正整数aia_iai​和bib_ibi​,表示一开始就存在一座连接岛aia_iai​和岛bib_ibi​的桥。后面剩下的部分描述操作,该部分的第一行是一个正整数qqq,表示一共有qqq个操作,接下来的qqq行依次描述每个操作,操作的格式如上所述,以大写字母QB开始,后面跟两个不超过nnn的正整数,字母与数字以及两个数字之间用空格隔开。

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

数据范围:
对于202020的数据n≤1000,q≤1000n≤1000,q≤1000n≤1000,q≤1000,
对于100100100的数据n≤100000,m≤n,q≤300000n≤100000,m≤n,q≤300000n≤100000,m≤n,q≤300000

由于要寻找与某个岛xxx的所有连通的岛里排名第kkk的岛的编号,所以想到要用平衡树,一开始有nnn个平衡树,然后每次合并两个树,构成更大的连通块(这可以用启发式合并来做,每次合并的时候将节点个数少的树后序遍历一遍,将每个节点插入到节点个数多的树里。注意,这里必须得后序遍历,这样插入的时候就可以直接拿原节点本身插入,而不需要new新节点了,可以节省很多空间。由于启发式合并里,每个节点参与合并的次数是O(log⁡n)O(\log n)O(logn),因为每个节点所在的树的节点个数会一次次翻倍,合并一次它需要O(log⁡n)O(\log n)O(logn),一共nnn个节点,所以合并上花的总时间复杂度为O(nlog⁡2n)O(n\log^2n)O(nlog2n));并且,为了查询排名,可以将每个树节点里存一下它为根的子树的节点个数。这两者都可以用FHQ Treap来做。但是,光有平衡树还不够,给定岛屿编号,我们要能知道这个岛屿现在在哪棵树里,进而得到树根,然后才能进行查询和合并的操作,这就需要并查集。代码如下:

#include <iostream>
using namespace std;// 开一倍空间即可
const int N = 1e5 + 10;
int n, m;
struct Node {int l, r;int val, id;int sz, rnd;
} tr[N];
int idx;// p是并查集数组,root[i]表示i号岛屿所在的FHQ Treap的
// 树根节点下标,只有在i是并查集树根的时候才有效
int root[N], p[N];int get_node(int val, int id) {tr[++idx].sz = 1;tr[idx].val = val;tr[idx].id = id;tr[idx].rnd = rand();return idx;
}void pushup(int u) {tr[u].sz = tr[tr[u].l].sz + tr[tr[u].r].sz + 1;
}int merge(int x, int y) {if (!x || !y) return x | y;if (tr[x].rnd > tr[y].rnd) {tr[x].r = merge(tr[x].r, y);pushup(x);return x;} else {tr[y].l = merge(x, tr[y].l);pushup(y);return y;}
}void split(int u, int val, int &x, int &y) {if (!u) x = y = 0;else {if (tr[u].val <= val) {x = u;split(tr[u].r, val, tr[u].r, y);} else {y = u;split(tr[u].l, val, x, tr[u].l);}pushup(u);}
}// 在编号为id的岛屿所在的FHQ Treap里查询排名第rank的节点的id
int get_id(int id, int rank) {int u = root[id];while (u) {if (rank <= tr[tr[u].l].sz) u = tr[u].l;else if (rank > tr[tr[u].l].sz + 1) {rank -= tr[tr[u].l].sz + 1;u = tr[u].r;} else return tr[u].id;}return -1;
}// 把下标为u的节点插入到b号岛屿所在的FHQ Treap里
void insert(int u, int b) {int x, y;// 按u的val将b所在的FHQ Treap分裂split(root[b], tr[u].val, x, y);// 再合并回来root[b] = merge(merge(x, u), y);
}// 后序遍历u,将每个节点插入编号为b的岛屿所在的树里
void dfs(int u, int b) {if (!u) return;dfs(tr[u].l, b);dfs(tr[u].r, b);insert(u, b);
}int find(int x) {if (p[x] != x) p[x] = find(p[x]);return p[x];
}int main() {scanf("%d%d", &n, &m);for (int i = 1; i <= n; i++) {int x;scanf("%d", &x);p[i] = root[i] = get_node(x, i);}while (m--) {int x, y;scanf("%d%d", &x, &y);x = find(x), y = find(y);if (x != y) {if (tr[root[x]].sz > tr[root[y]].sz) 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 (tr[root[x]].sz > tr[root[y]].sz) swap(x, y);p[x] = y;dfs(root[x], y);}} else {int k = y;if (tr[root[x]].sz < k) puts("-1");else printf("%d\n", get_id(x, k));}}return 0;
}

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

也可以用Splay来做。代码如下:

#include <iostream>
using namespace std;const int N = 1e5 + 10;
int n, m;
struct Node {int s[2], p, v, id;int sz;void init(int _v, int _id, int _p) {v = _v, id = _id, p = _p;sz = 1;}
} tr[N];
int root[N], idx;
int p[N];int find(int x) {if (p[x] != x) p[x] = find(p[x]);return p[x];
}void pushup(int x) {tr[x].sz = tr[tr[x].s[0]].sz + tr[tr[x].s[1]].sz + 1;
}void rotate(int x) {int y = tr[x].p, z = tr[y].p;int k = tr[y].s[1] == x;tr[z].s[tr[z].s[1] == y] = x, tr[x].p = z;tr[y].s[k] = tr[x].s[k ^ 1], tr[tr[x].s[k ^ 1]].p = y;tr[x].s[k ^ 1] = y, tr[y].p = x;pushup(y), pushup(x);
}void splay(int x, int k, int b) {while (tr[x].p != k) {int y = tr[x].p, z = tr[y].p;if (z != k)if ((tr[y].s[1] == x) ^ (tr[z].s[1] == y)) rotate(x);else rotate(y);rotate(x);}if (!k) root[b] = x;
}void insert(int u, int b) {int c = root[b], v = tr[u].v, p = 0;while (c) p = c, c = tr[c].s[v > tr[c].v];if (p) tr[p].s[v > tr[p].v] = u;tr[u].p = p;// 插入完了要将插入的节点伸展到树根splay(u, 0, b);
}int get_k(int k, int b) {int u = root[b];while (u) {if (k <= tr[tr[u].s[0]].sz) u = tr[u].s[0];else if (k > tr[tr[u].s[0]].sz + 1) {k -= tr[tr[u].s[0]].sz + 1;u = tr[u].s[1];} else return tr[u].id;}return -1;
}void dfs(int u, int b) {if (!u) return;if (tr[u].s[0]) dfs(tr[u].s[0], b);if (tr[u].s[1]) dfs(tr[u].s[1], b); insert(u, b);
}int main() {scanf("%d%d", &n, &m);for (int i = 1; i <= n; i++) {p[i] = root[i] = i;int v;scanf("%d", &v);tr[i].init(v, i, 0);}idx = n;while (m--) {int a, b;scanf("%d%d", &a, &b);a = find(a), b = find(b);if (a != b) {if (tr[root[a]].sz > tr[root[b]].sz) swap(a, b);p[a] = b;dfs(root[a], b);}}scanf("%d", &m);while (m--) {char op[2];int a, b;scanf("%s%d%d", op, &a, &b);a = find(a);if (*op == 'B') {b = find(b);if (a != b) {if (tr[root[a]].sz > tr[root[b]].sz) swap(a, b);p[a] = b;dfs(root[a], b);}} else {if (tr[root[a]].sz < b) puts("-1");else printf("%d\n", get_k(b, a));}}return 0;
}

时空复杂度一样。

【ACWing】1063. 永无乡相关推荐

  1. BZOJ 2733: [HNOI2012]永无乡 [splay启发式合并]

    2733: [HNOI2012]永无乡 题意:加边,询问一个连通块中k小值 终于写了一下splay启发式合并 本题直接splay上一个节点对应图上一个点就可以了 并查集维护连通性 合并的时候,把siz ...

  2. 数据结构之线段树合并——永无乡,Lomsat gelral,Tree Rotations,Tree Rotations Escape Through Leaf

    文章目录 [HNOI2012]永无乡 Lomsat gelral 「POI2011 R2 Day2」旋转树木 Tree Rotations Escape Through Leaf 线段树合并与 fhq ...

  3. 数据结构之fhq-treap——Chef and Sets,[HNOI2012]永无乡,Play with Chain,[NOI2005]维修数列(结构体版代码)

    因为非常板,所以主要是代码 Tyvj 1728 普通平衡树 Chef and Sets [HNOI2012]永无乡 Play with Chain [NOI2005]维修数列 题目很水,所以可能会出现 ...

  4. P3224 [HNOI2012]永无乡(并查集+权值线段树合并/平衡树)

    [HNOI2012]永无乡 Code1 权值线段树天然支持merge,线段树上二分求第k小 #include<bits/stdc++.h>using namespace std; usin ...

  5. bzoj2733永无乡

    永无乡 HYSBZ - 2733 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接, ...

  6. BZOJ 2733: [HNOI2012]永无乡 启发式合并treap

    2733: [HNOI2012]永无乡 Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/pr ...

  7. bzoj 2733: [HNOI2012]永无乡(线段树启发式合并)

    2733: [HNOI2012]永无乡 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 3850  Solved: 2061 [Submit][Sta ...

  8. [BZOJ2733] [HNOI2012] 永无乡 (splay启发式合并)

    Description 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以 ...

  9. 洛谷P3224【HNOI2012】永无乡

    洛谷P3224[HNOI2012]永无乡 题目大意 有 n n n个点,每个点都有一个重要度.先连接 m m m条边,然后有 q q q次操作: B x y 表示连接点 x x x和点 y y y Q ...

最新文章

  1. linux/docker个人服务器项目中文变问号??,时间差8小时问题解决方法,最新,最有效
  2. Django models的诡异异常RelatedObjectDoesNotExist
  3. 转发高人文章:以前写的一些有关代码签名/时间戳数字证书的东东
  4. 数据中心(机房)施工方案
  5. 在非洲意外当上酋长的河南小伙,现在咋样了?
  6. pytorch中查看可训练参数
  7. 软件工程——进展记录
  8. 微信小程序云开发教程-小程序代码发布和上线运行
  9. 开源自动化运维工具_批量与重复运维压力如何破?了解一下这款自动化运维工具...
  10. 人工智能兴起的条件_跟我说规矩。 AI受虐狂的兴起
  11. 12306火车票查询——Python
  12. 【前端换肤】前端换肤方案
  13. C语言中用do while解决阶乘之和问题
  14. python有什么游戏可以开发-主流游戏引擎有哪些?python能开发手游?
  15. e5408fc4a618ed2a663d0306def2cec3 (学生实验,谢谢)
  16. 《数据库系统概念》第六版官方英文答案
  17. zTree 一个依靠 jQuery 实现的多功能 “树插件”
  18. 华为云WeLink数字化人才高研班在杭州开班
  19. ati显卡故障测试软件,amd显卡自动检测
  20. 游戏账号安全攻略:如何减少账号被盗的风险?

热门文章

  1. mysql 一 、关系模型——主键——外键——索引
  2. Dapper 下划线
  3. 开发实习日记10.11
  4. 事务4个隔离界别及脏读,不可重复读,幻读
  5. Expanding Low-Density Latent Regions for Open-Set Object Detection
  6. 利用python实现利用追赶法解决线性方程组
  7. 匹配问题: 匈牙利算法 、最优指派、相等子图、库恩—曼克莱斯 (Kuhn-Munkres) 算法
  8. Linux安装Microsoft Windows Fonts微软字体库
  9. 什么是反向代理服务器
  10. 科比自传读后感 --曼巴精神