洛谷 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大值最初的集合编号. 思路 维护集合之间关 ...
随机推荐
- 【复杂度分析】loj#6043. 「雅礼集训 2017 Day7」蛐蛐国的修墙方案
感觉有点假 题目大意 数据范围:$n<=100$ 题目分析 由于题目给出的是 置换,所以相当于只需枚举每个环的两个状态. 主要是复杂度分析这里: 一元环:不存在 二元环:特判保平安 三元环:不存 ...
- vim 个性化设置和操作
一.vim 设置 1. 设置行号显示 1) 临时显示 命令行模式 :set nu 2) 永久显示 # vim ~/.vimrc 插入一行代码: set number 若没有该文件,在用户主目录 (/h ...
- datatable设置动态宽度,超过一定长度出现滚动条
获得宽度:var tableAutoWidth = $('.dataTable_wrapper').width();if (tableAutoWidth < 1200) { tableAutoW ...
- JavaScript高级程序设计第三版.CHM【带实例】
从驱动全球商业.贸易及管理领域不计其数的复杂应用程序的角度来看,说 JavaScript 已经成为当今世界上最流行的编程语言一点儿都不为过. JavaScript 是一种非常松散的面向对象语言,也是 ...
- you do not permission to access / no this server
最近在学习Linux下的httpd服务,自己写了一个虚拟主机的配置文件 重启之后,怎么访问都是出现以下的内容, do not permission to access / no this server ...
- Postgres常用命令之增、删、改、查
增.删.改.查: postgres=# \password postgres 为postgres进行密码设置: postgres=# CREATE USER test WITH PASSWORD '1 ...
- 机器学习实战 -- 决策树(ID3)
机器学习实战 -- 决策树(ID3) ID3是什么我也不知道,不急,知道他是干什么的就行 ID3是最经典最基础的一种决策树算法,他会将每一个特征都设为决策节点,有时候,一个数据集中,某些特征属 ...
- html_outputer.py
coding=UTF-8 # HTML输出器 import sys class htmlOutputer(): def __init__(self): self.data = [] def colle ...
- sprintf()函数使用异常
调试STM32F103,比如如下代码:使用springf函数,这个函数是把最后两个参数先格式化成字符串 ,输出到ERROR_STRING,如果他们合并的长度大于30会出现深情况? ] sprintf( ...
- Java算法求最大最小值,倒序,冒泡排序,斐波纳契数列,日历一些经典算法
一,求最大,最小值 int[] a={21,31,4,2,766,345,2,34}; //这里防止数组中有负数,所以初始化的时候给的数组中的第一个数. int max=a[0]; int min=a ...