bzoj 2733 永无乡 - 并查集 - 线段树
永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示。某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛。如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的。现在有两种操作:B x y 表示在岛 x 与岛 y 之间修建一座新桥。Q x k 表示询问当前与岛 x连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k 小的岛是哪 座,请你输出那个岛的编号。
Input
输入文件第一行是用空格隔开的两个正整数 n 和 m,分别 表示岛的个数以及一开始存在的桥数。接下来的一行是用空格隔开的 n 个数,依次描述从岛 1 到岛 n 的重要度排名。随后的 m 行每行是用空格隔开的两个正整数 ai 和 bi,表示一开始就存 在一座连接岛 ai 和岛 bi 的桥。后面剩下的部分描述操作,该部分的第一行是一个正整数 q, 表示一共有 q 个操作,接下来的 q 行依次描述每个操作,操作的格式如上所述,以大写字母 Q 或B 开始,后面跟两个不超过 n 的正整数,字母与数字以及两个数字之间用空格隔开。 对于 20%的数据 n≤1000,q≤1000 对于 100%的数据 n≤100000,m≤n,q≤300000
Output
对于每个 Q x k 操作都要依次输出一行,其中包含一个整数,表 示所询问岛屿的编号。如果该岛屿不存在,则输出-1。
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
Sample Output
- -1
- 2
- 5
- 1
- 2
题目大意 给出n个点和m条边,每个点有个点权,范围为1 ~ n,且没有重复。操作有两种,第一种是联通两个点,第二种是查询点i所在的连通块内第k小的点的编号。
显然会用到并查集,显然线段树合并。
然后我来讲讲线段树合并的时间复杂度证明。
一次线段树合并的实际代价是减少的节点个数。
开始有$O(n\log n)$个节点,最后只有$O(n)$个点,所以时间复杂度为$O(n\log n)$。
Code
- /**
- * bzoj
- * Problem#2733
- * Accepted
- * Time:2352ms
- * Memory:26304k
- */
- #include <iostream>
- #include <cstdio>
- #include <ctime>
- #include <cmath>
- #include <cctype>
- #include <cstring>
- #include <cstdlib>
- #include <fstream>
- #include <sstream>
- #include <algorithm>
- #include <map>
- #include <set>
- #include <stack>
- #include <queue>
- #include <vector>
- #include <stack>
- #ifndef WIN32
- #define Auto "%lld"
- #else
- #define Auto "%I64d"
- #endif
- using namespace std;
- typedef bool boolean;
- const signed int inf = (signed)((1u << ) - );
- const signed long long llf = (signed long long)((1ull << ) - );
- const double eps = 1e-;
- const int binary_limit = ;
- #define smin(a, b) a = min(a, b)
- #define smax(a, b) a = max(a, b)
- #define max3(a, b, c) max(a, max(b, c))
- #define min3(a, b, c) min(a, min(b, c))
- template<typename T>
- inline boolean readInteger(T& u){
- char x;
- int aFlag = ;
- while(!isdigit((x = getchar())) && x != '-' && x != -);
- if(x == -) {
- ungetc(x, stdin);
- return false;
- }
- if(x == '-'){
- x = getchar();
- aFlag = -;
- }
- for(u = x - ''; isdigit((x = getchar())); u = (u * ) + x - '');
- ungetc(x, stdin);
- u *= aFlag;
- return true;
- }
- typedef class SegTreeNode {
- public:
- int s;
- SegTreeNode *l, *r;
- SegTreeNode():s(), l(NULL), r(NULL) { }
- SegTreeNode(int s, SegTreeNode* l, SegTreeNode* r):s(s), l(l), r(r) { }
- inline void pushUp() {
- s = l->s + r->s;
- }
- }SegTreeNode;
- #define Limit 2000000
- SegTreeNode pool[Limit];
- int top = ;
- SegTreeNode null = SegTreeNode(, &null, &null);
- inline SegTreeNode* newnode() {
- if(top >= Limit) {
- return new SegTreeNode(, &null, &null);
- }
- pool[top].l = &null, pool[top].r = &null;
- return pool + (top++);
- }
- #define null &null
- typedef class union_found {
- public:
- int* f;
- SegTreeNode** rts;
- union_found():f(NULL), rts(NULL) { }
- union_found(int n, int* lis) {
- f = new int[(const int)(n + )];
- rts = new SegTreeNode*[(n + )];
- for(int i = ; i <= n; i++)
- f[i] = i;
- for(int i = ; i <= n; i++) {
- rts[i] = null;
- update(rts[i], , n, lis[i]);
- }
- }
- void update(SegTreeNode*& node, int l, int r, int idx) {
- if(node == null) node = newnode();
- if(l == idx && r == idx) {
- node->s++;
- return;
- }
- int mid = (l + r) >> ;
- if(idx <= mid) update(node->l, l, mid, idx);
- else update(node->r, mid + , r, idx);
- node->pushUp();
- }
- void merge(SegTreeNode*& a, SegTreeNode*& b) { // Make segment tree b into a.
- if(b == null)
- return;
- if(a == null) {
- a = b;
- return;
- }
- a->s += b->s;
- merge(a->l, b->l);
- merge(a->r, b->r);
- a->pushUp();
- }
- int kth(SegTreeNode* node, int l, int r, int k) {
- if(node == null)
- return -;
- if(l == r && k == )
- return l;
- int ls = node->l->s, mid = (l + r) >> ;
- if(k <= ls)
- return kth(node->l, l, mid, k);
- else
- return kth(node->r, mid + , r, k - ls);
- }
- int find(int x) {
- return (f[x] == x) ? (x) : (f[x] = find(f[x]));
- }
- void unit(int fa, int so) {
- if(isConnected(fa, so)) return;
- int ffa = find(fa);
- int fso = find(so);
- merge(rts[ffa], rts[fso]);
- f[fso] = ffa;
- }
- boolean isConnected(int a, int b) {
- return find(a) == find(b);
- }
- int query(int x, int n, int k) {
- return kth(rts[find(x)], , n, k);
- }
- }union_found;
- int n, m, q;
- int* imp;
- int* keyer;
- union_found uf;
- inline void init() {
- readInteger(n);
- readInteger(m);
- imp = new int[(n + )];
- keyer = new int[(n + )];
- for(int i = ; i <= n; i++)
- readInteger(imp[i]), keyer[imp[i]] = i;
- uf = union_found(n, imp);
- for(int i = , a, b; i <= m; i++) {
- readInteger(a);
- readInteger(b);
- uf.unit(a, b);
- }
- }
- inline void solve() {
- readInteger(q);
- char buf[];
- int a, b;
- while(q--) {
- scanf("%s%d%d", buf, &a, &b);
- if(buf[] == 'B') {
- uf.unit(a, b);
- } else {
- int res = uf.query(a, n, b);
- if(res == -)
- puts("-1");
- else
- printf("%d\n", keyer[res]);
- }
- }
- }
- int main() {
- init();
- solve();
- return ;
- }
bzoj 2733 永无乡 - 并查集 - 线段树的更多相关文章
- BZOJ2733 [HNOI2012]永无乡(并查集+线段树合并)
题目大意: 在$n$个带权点上维护两个操作: 1)在点$u,v$间连一条边: 2)询问点$u$所在联通块中权值第$k$小的点的编号,若该联通块中的点的数目小于$k$,则输出$-1$: 传送门 上周的模 ...
- B20J_2733_[HNOI2012]永无乡_权值线段树合并
B20J_2733_[HNOI2012]永无乡_权值线段树合并 Description:n座岛,编号从1到n,每座岛都有自己的独一无二的重要度,按照重要度可以将这n座岛排名,名次用1到 n来表示.某些 ...
- [bzoj2733][HNOI2012]永无乡_权值线段树_线段树合并
永无乡 bzoj-2733 HNOI-2012 题目大意:题目链接. 注释:略. 想法: 它的查询操作非常友善,就是一个联通块内的$k$小值. 故此我们可以考虑每个联通块建一棵权值线段树. 这样的话每 ...
- 【洛谷P3224】永无乡 并查集+Splay启发式合并
题目大意:给定 N 个点的图,点有点权,初始有一些无向边,现在有 Q 个询问,每个询问支持动态增加一条无向边连接两个不连通的点和查询第 X 个点所在的联通块中权值第 K 大的是哪个点. 题解:学会了平 ...
- BZOJ 2733 永无乡
splay启发式合并 启发式合并其实就是把集合数量小的合并到集合数量大的里去. 怎么合并呢,直接一个一个插入就行了.. 用并查集维护连通性,find(i)可以找到所在splay的编号 这题好像还可以合 ...
- bzoj 2733 永无乡 线段树
题目: 支持两种操作: 合并两点所在的联通块 查询某点所在联通块内权值第k小. 题解 平衡树启发式合并随便搞一搞就好了. 我写了一个线段树合并 #include <cstdio> #inc ...
- BZOJ 3910 并查集+线段树合并
思路: 1. 并查集+线段树合并 记得f[LCA]==LCA的时候 f[LCA]=fa[LCA] 2.LCT(并不会写啊...) //By SiriusRen #include <cstdio& ...
- UVA1455 - Kingdom(并查集 + 线段树)
UVA1455 - Kingdom(并查集 + 线段树) 题目链接 题目大意:一个平面内,给你n个整数点,两种类型的操作:road x y 把city x 和city y连接起来,line fnum ...
- 并查集&线段树&树状数组&排序二叉树
超级无敌巨牛逼并查集(带权并查集)https://vjudge.net/problem/UVALive-4487 带删点的加权并查集 https://vjudge.net/problem/UVA-11 ...
随机推荐
- 好吧,我承认我是爱瞎折腾----利用YDUI改变页面UI
上周恒丰代付接口上线投产后,我做了一个“恒丰代付检查工具”,用途是,当线上调用恒丰代付出现了问题订单时,可以在这个工具页里做相应的弥补. 我项目里其他一些工具页的UI用的是YDUI.YDUI号称是“一 ...
- php 门面模式
1.门面模式为外部提供一个统一的接口,外部调用者不用知道内部的具体复杂业务. 2.如果不使用门面模式,直接访问内部系统,会造成相互直接的耦合关系, 3.想让你的子系统开通哪些,就开通哪些,在门面上开通 ...
- Unity shader学习之Alpha Test
可以在fragment中使用cg的函数--clip来进行透明度测试. 函数定义如下: void clip(float4 x); void clip(float3 x); void clip(float ...
- armv8 memory translation table descriptor
上一节大致给出了descriptor的结构,这篇细致分析各个field: 1) Table Descriptor:stage2中不包含任何的attribute的field,每个level中的descr ...
- 设置一个div网页滚动时,使其固定在头部,当页面滚动到距离头部300px时,隐藏该div,另一个div在底部,此时显示;当页面滚动到起始位置时,头部div出现,底部div隐藏
设置一个div网页滚动时,使其固定在头部,当页面滚动到距离头部300px时,隐藏该div,另一个div在底部,此时显示: 当页面滚动到起始位置时,头部div出现,底部div隐藏 前端代码: <! ...
- 20170918-00-(代理ip检验)
代码集编号 20170918-00 import random #随机数模块 import urllib.request #常用爬虫模块 import time from bs4 import Bea ...
- python实现堆栈和队列
利用python列表实现堆栈和队列 堆栈: 堆栈是一个后进先出的数据结构,其工作方式就像生活中常见到的直梯,先进去的人肯定是最后出. 我们可以设置一个类,用列表来存放栈中的元素的信息,利用列表的app ...
- MapReduce的map个数调节 与 Hadoop的FileInputFormat的任务切分原理
在对日志等大表数据进行处理的时候需要人为地设置任务的map数,防止因map数过小导致集群资源被耗光.可根据大表的数据量大小设置每个split的大小. 例如设置每个split为500M: set map ...
- 调用微信JS-SDK配置签名
前后端进行分开开发: 1:后端实现获取 +++接口凭证:access_token (公众号的全局唯一接口调用凭据) ** GET 获取:https://api.weixin.qq.com/cgi-bi ...
- Codeforce 835A - Key races
Two boys decided to compete in text typing on the site "Key races". During the competition ...