洛谷 P3224 [HNOI2012]永无乡 解题报告
P3224 [HNOI2012]永无乡
题目描述
永无乡包含 \(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\) 行每行是用空格隔开的两个正整数 \(a_i\)和 \(b_i\),表示一开始就存在一座连接岛 \(a_i\) 和岛 \(b_i\) 的桥。
后面剩下的部分描述操作,该部分的第一行是一个正整数 \(q\) ,表示一共有 \(q\) 个操作,接下来的 \(q\) 行依次描述每个操作,操作的 格式如上所述,以大写字母 \(Q\) 或 \(B\) 开始,后面跟两个不超过 \(n\) 的正整数,字母与数字以及两个数字之间用空格隔开。
输出格式:
对于每个 Q x k 操作都要依次输出一行,其中包含一个整数,表示所询问岛屿的编号。如果该岛屿不存在,则输出 \(−1\) 。
说明
对于 \(20\%\) 的数据 \(n \leq 1000, q \leq 1000\)
对于 \(100\%\) 的数据 \(n \leq 100000, m \leq n, q \leq 300000\)
题意:点有点权,维护连通块的第\(k\)值,支持加边操作
维护第\(k\)值可以使用主席树平衡树等,这里我使用主席树
加边额外并查集维护,合并直接启发式合并,查询直接查询即可
复杂度分析
设所有的块都被合并到一块去了,且每次合并两个块时它们的大小相等
则有式子
\(T(n)=2\times T(\frac{n}{2})+\frac{n}{2}log\frac{n}{2}\)
合并的复杂度是那个是因为一个有\(s\)大小的块的主席树的链的条数有\(s\)条
updata:又玩了玩复杂度,发现似乎最坏复杂度是这样,但不能这么简单的说明,我自己也不太清楚,当做大约是\(n\)个节点的合并的复杂度在\(O(n)-O(nlogm)\)左右的吧,如果有什么能说清楚复杂度的,烦请跟我说一声QAQ
总复杂度:\(O(nlog^2n)\)
Code:
#include <cstdio>
#include <algorithm>
const int N=1e5+10;
int a[N],b[N],num[N];
int root[N],ch[N*20][2],sum[N*20],tot,f[N],siz[N],n,m,q;
#define ls ch[now][0]
#define rs ch[now][1]
void updata(int now){sum[now]=sum[ls]+sum[rs];}
int build(int l,int r,int pos)
{
int now=++tot;
if(l==r)
{
sum[now]=1;
return now;
}
int mid=l+r>>1;
if(pos<=mid) ls=build(l,mid,pos);
else rs=build(mid+1,r,pos);
updata(now);
return now;
}
int Find(int x){return f[x]==x?x:Find(f[x]);}
#define ols ch[las][0]
#define ors ch[las][1]
void merge(int &now,int las,int l,int r)
{
if(!now) {now=las;return;}
if(l==r)
{
sum[now]+=sum[las];
return;
}
int mid=l+r>>1;
if(ols) merge(ls,ols,l,mid);
if(ors) merge(rs,ors,mid+1,r);
updata(now);
}
void swap(int &x,int &y){x+=y,y=-y,y+=x,x-=y;}
void Merge(int x,int y)
{
x=Find(x),y=Find(y);
if(x==y) return;
if(siz[x]<siz[y]) swap(x,y);
merge(root[x],root[y],1,n);
siz[x]+=siz[y];
f[y]=x;
}
int query(int now,int l,int r,int k)
{
if(sum[now]<k) return -1;
if(l==r) return num[l];
int mid=l+r>>1;
if(sum[ls]>=k) return query(ls,l,mid,k);
else return query(rs,mid+1,r,k-sum[ls]);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",a+i),b[i]=a[i];
std::sort(a+1,a+1+n);
for(int i=1;i<=n;i++)
b[i]=std::lower_bound(a+1,a+1+n,b[i])-a;
for(int i=1;i<=n;i++)
num[b[i]]=i,f[i]=i,siz[i]=1,root[i]=build(1,n,b[i]);
for(int u,v,i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
Merge(u,v);
}
scanf("%d",&q);
char op[5];
for(int u,v,i=1;i<=q;i++)
{
scanf("%s%d%d",op,&u,&v);
if(op[0]=='B') Merge(u,v);
else printf("%d\n",query(root[Find(u)],1,n,v));
}
return 0;
}
2018.9.3
洛谷 P3224 [HNOI2012]永无乡 解题报告的更多相关文章
- 洛谷P3224 [HNOI2012]永无乡(线段树合并+并查集)
题目描述 永无乡包含 nnn 座岛,编号从 111 到 nnn ,每座岛都有自己的独一无二的重要度,按照重要度可以将这 nnn 座岛排名,名次用 111 到 nnn 来表示.某些岛之间由巨大的桥连接, ...
- 洛谷 P3224 [HNOI2012]永无乡
题面 永无乡包含 \(n\) 座岛,编号从 \(1\) 到 \(n\) ,每座岛都有自己的独一无二的重要度,按照重要度可以将这 \(n\) 座岛排名,名次用 \(1\) 到 \(n\) 来表示.某些岛 ...
- [洛谷P3224][HNOI2012]永无乡
题目大意:给你$n$个点,每个点有权值$k$,现有两种操作: 1. $B\;x\;y:$将$x,y$所在联通块合并2. $Q\;x\;k:$查询第$x$个点所在联通块权值第$k$小是哪个数 题解:线段 ...
- 2018.08.11 洛谷P3224 [HNOI2012]永无乡(线段树合并)
传送门 给出n个带点权的点,支持连边和查询连通块第k大. 这个貌似就是一道线段树合并的裸板啊... 代码: #include<bits/stdc++.h> #define N 100005 ...
- [HNOI2012] 永无乡 解题报告 (splay+启发式合并)
题目链接:https://www.luogu.org/problemnew/show/P3224#sub 题目: 题目大意: 维护多个联通块,没有删除操作,每次询问某一联通块的第k大 解法: 维护联通 ...
- 【洛谷P3224】永无乡 并查集+Splay启发式合并
题目大意:给定 N 个点的图,点有点权,初始有一些无向边,现在有 Q 个询问,每个询问支持动态增加一条无向边连接两个不连通的点和查询第 X 个点所在的联通块中权值第 K 大的是哪个点. 题解:学会了平 ...
- 洛谷.3224.[HNOI2012]永无乡(Splay启发式合并)
题目链接 查找排名为k的数用平衡树 合并时用启发式合并,把size小的树上的所有节点插入到size大的树中,每个节点最多需要O(logn)时间 并查集维护连通关系即可 O(nlogn*insert t ...
- 线段树合并+并查集 || BZOJ 2733: [HNOI2012]永无乡 || Luogu P3224 [HNOI2012]永无乡
题面:P3224 [HNOI2012]永无乡 题解: 随便写写 代码: #include<cstdio> #include<cstring> #include<iostr ...
- P3224 [HNOI2012]永无乡 题解
P3224 [HNOI2012]永无乡 题解 题意概括 有若干集合,每个集合最初包含一个值,和一个编号1~n.两个操作:合并两个集合,查询包含值x的集合中第k大值最初的集合编号. 思路 维护集合之间关 ...
随机推荐
- 【赛时总结】 ◇赛时·I◇ AtCoder ARC-098
◆赛时I◆ ARC-098 ■试题&解析■ ◆本场最水◆ C-Attention 长点儿信心吧-- [AtCoder ARC-098 C] [解析] 既然只存在左右(东西)两个朝向,那么领导右 ...
- js将人民币数字转大写
function numberToUpper(money) { var cnNums = new Array("零", "壹", "贰", ...
- Salt-ssh 自动安装salt-minion
作用:为了不手动去安装一台一台去salt-minion,并进重复的配置 一.环境 系统环境: #cat /etc/redhat-release CentOS Linux release 7.4.170 ...
- 用ajax获取淘宝关键字接口
可定需要查看淘宝界面的结构,按F12查看网页,此时先清除一下网页中的数据,让Network制空,随后在输入框中输入新的内容,比如钱包,数据中会出现新的数据.点击及查看蓝色方框中的内容 点击之后,你可以 ...
- PHP 基础知识总结
PHP 代表 PHP: Hypertext Preprocessor PHP 文件可包含文本.HTML.JavaScript代码和 PHP 代码 PHP 代码在服务器上执行,结果以纯 HTML 形式返 ...
- 解决scp命令pemission denied,please try again的问题
问题描述:输入命令scp a.txt root@192.168.0.105:/tmp(将当前目录下的文件a.txt复制到服务器IP为192.168.0.105的root用户的/tmp/目录下),结果会 ...
- Altium Designer使用5:AD18的DXP在什么地方?
1.在顶上的菜单栏右击
- C语言进阶——分支语句06
if分支语句分析: if语句用于根据条件选择执行语句 else不能独立存在且总是与在它之前的最近if相匹配 esle语句后可以连接其他if语句 用法如下: if(condition) { //stat ...
- [Bzoj3252]攻略(dfs序+线段树)
Description 题目链接 Solution 可以想到,每次肯定是拿最大价值为最优 考虑改变树上一个点的值,只会影响它的子树,也就是dfs序上的一个区间, 于是可以以dfs序建线段树,这样就变成 ...
- Hive LanguageManual DDL
hive语法规则LanguageManual DDL SQL DML 和 DDL 数据操作语言 (DML) 和 数据定义语言 (DDL) 一.数据库 增删改都在文档里说得也很明白,不重复造车轮 二.表 ...