【ACWing】1063. 永无乡
题目地址:
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行依次描述每个操作,操作的格式如上所述,以大写字母Q
或B
开始,后面跟两个不超过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(logn)O(\log n)O(logn),因为每个节点所在的树的节点个数会一次次翻倍,合并一次它需要O(logn)O(\log n)O(logn),一共nnn个节点,所以合并上花的总时间复杂度为O(nlog2n)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(nlog2n+mlogn)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. 永无乡相关推荐
- BZOJ 2733: [HNOI2012]永无乡 [splay启发式合并]
2733: [HNOI2012]永无乡 题意:加边,询问一个连通块中k小值 终于写了一下splay启发式合并 本题直接splay上一个节点对应图上一个点就可以了 并查集维护连通性 合并的时候,把siz ...
- 数据结构之线段树合并——永无乡,Lomsat gelral,Tree Rotations,Tree Rotations Escape Through Leaf
文章目录 [HNOI2012]永无乡 Lomsat gelral 「POI2011 R2 Day2」旋转树木 Tree Rotations Escape Through Leaf 线段树合并与 fhq ...
- 数据结构之fhq-treap——Chef and Sets,[HNOI2012]永无乡,Play with Chain,[NOI2005]维修数列(结构体版代码)
因为非常板,所以主要是代码 Tyvj 1728 普通平衡树 Chef and Sets [HNOI2012]永无乡 Play with Chain [NOI2005]维修数列 题目很水,所以可能会出现 ...
- P3224 [HNOI2012]永无乡(并查集+权值线段树合并/平衡树)
[HNOI2012]永无乡 Code1 权值线段树天然支持merge,线段树上二分求第k小 #include<bits/stdc++.h>using namespace std; usin ...
- bzoj2733永无乡
永无乡 HYSBZ - 2733 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接, ...
- BZOJ 2733: [HNOI2012]永无乡 启发式合并treap
2733: [HNOI2012]永无乡 Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/pr ...
- bzoj 2733: [HNOI2012]永无乡(线段树启发式合并)
2733: [HNOI2012]永无乡 Time Limit: 10 Sec Memory Limit: 128 MB Submit: 3850 Solved: 2061 [Submit][Sta ...
- [BZOJ2733] [HNOI2012] 永无乡 (splay启发式合并)
Description 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以 ...
- 洛谷P3224【HNOI2012】永无乡
洛谷P3224[HNOI2012]永无乡 题目大意 有 n n n个点,每个点都有一个重要度.先连接 m m m条边,然后有 q q q次操作: B x y 表示连接点 x x x和点 y y y Q ...
最新文章
- linux/docker个人服务器项目中文变问号??,时间差8小时问题解决方法,最新,最有效
- Django models的诡异异常RelatedObjectDoesNotExist
- 转发高人文章:以前写的一些有关代码签名/时间戳数字证书的东东
- 数据中心(机房)施工方案
- 在非洲意外当上酋长的河南小伙,现在咋样了?
- pytorch中查看可训练参数
- 软件工程——进展记录
- 微信小程序云开发教程-小程序代码发布和上线运行
- 开源自动化运维工具_批量与重复运维压力如何破?了解一下这款自动化运维工具...
- 人工智能兴起的条件_跟我说规矩。 AI受虐狂的兴起
- 12306火车票查询——Python
- 【前端换肤】前端换肤方案
- C语言中用do while解决阶乘之和问题
- python有什么游戏可以开发-主流游戏引擎有哪些?python能开发手游?
- e5408fc4a618ed2a663d0306def2cec3 (学生实验,谢谢)
- 《数据库系统概念》第六版官方英文答案
- zTree 一个依靠 jQuery 实现的多功能 “树插件”
- 华为云WeLink数字化人才高研班在杭州开班
- ati显卡故障测试软件,amd显卡自动检测
- 游戏账号安全攻略:如何减少账号被盗的风险?