P3224 [HNOI2012] —— 永无乡
[HNOI2012] 永无乡
题目描述
永无乡包含 座岛,编号从 到 ,每座岛都有自己的独一无二的重要度,按照重要度可以将这 座岛排名,名次用 到 来表示。某些岛之间由巨大的桥连接,通过桥可以从一个岛到达另一个岛。如果从岛 出发经过若干座(含 座)桥可以 到达岛 ,则称岛 和岛 是连通的。
现在有两种操作:
B x y
表示在岛 与岛 之间修建一座新桥。
Q x k
表示询问当前与岛 连通的所有岛中第 重要的是哪座岛,即所有与岛 连通的岛中重要度排名第 小的岛是哪座,请你输出那个岛的编号。
输入格式
第一行是用空格隔开的两个整数,分别表示岛的个数 以及一开始存在的桥数 。
第二行有 个整数,第 个整数表示编号为 的岛屿的排名 。
接下来 行,每行两个整数 ,表示一开始存在一座连接编号为 的岛屿和编号为 的岛屿的桥。
接下来一行有一个整数,表示操作个数 。
接下来 行,每行描述一个操作。每行首先有一个字符 ,表示操作类型,然后有两个整数 。
- 若 为
Q
,则表示询问所有与岛 连通的岛中重要度排名第 小的岛是哪座,请你输出那个岛的编号。 - 若 为
B
,则表示在岛 与岛 之间修建一座新桥。
输出格式
对于每个询问操作都要依次输出一行一个整数,表示所询问岛屿的编号。如果该岛屿不存在,则输出 。
样例 #1
样例输入 #1
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
样例输出 #1
-1 2 5 1 2
提示
数据规模与约定
- 对于 的数据,保证 , 。
- 对于 的数据,保证 , , 为一个 的排列,,。
分析
并查集维护连通性,线段树合并。 数组记录根编号为 的联通块的权值线段树。
每次修改合并直接合并两个联通块的信息。
注意线段树合并不能自己合并自己,会导致叶节点权值错误影响答案。
#include<bits/stdc++.h> using namespace std; const int N=2e5+100; int n,m,q; int g[N],f[N]; int root[N<<5],tot,rk[N]; int fin(int x){return (f[x]==x)?(x):(f[x]=fin(f[x]));} struct segtree{int lc,rc,val,id;}s[N<<5]; void upd(int &i,int l,int r,int x) { if(!i)i=++tot; ++s[i].val; if(l==r)return ; int mid=(l+r)>>1; if(x<=mid)upd(s[i].lc,l,mid,x); else upd(s[i].rc,mid+1,r,x); } void merg(int &i,int &j,int l,int r)//i<--j { if(i==0 || j==0){i+=j;return ;} if(l==r) { s[i].val+=s[j].val; return ; } int mid=(l+r)>>1; merg(s[i].lc,s[j].lc,l,mid); merg(s[i].rc,s[j].rc,mid+1,r); s[i].val=s[s[i].lc].val+s[s[i].rc].val; } void init() { scanf("%d%d",&n,&m); for(int i=1,x;i<=n;++i) { scanf("%d",&x); upd(root[i],1,n,x); f[i]=i; rk[x]=i; } for(int i=1,x,y,fx,fy;i<=m;++i) { scanf("%d%d",&x,&y); fx=fin(x); fy=fin(y); if(fx!=fy) { merg(root[fx],root[fy],1,n); f[fy]=fx; } } } int que(int i,int l,int r,int k) { if(i==0 && k>0)return -1; if(l==r) return (s[i].val==k)?(l):(-1); int mid=(l+r)>>1,sum=s[s[i].lc].val; if(sum>=k)return que(s[i].lc,l,mid,k); else return que(s[i].rc,mid+1,r,k-sum); } void work() { scanf("%d",&q); while(q--) { char t=getchar(); int x,y; while(t!='Q' && t!='B')t=getchar(); scanf("%d%d",&x,&y); if(t=='Q') { int ans=que(root[fin(x)],1,n,y); if(ans==-1)printf("-1\n"); else printf("%d\n",rk[ans]); } else { int fx=fin(x),fy=fin(y); if(fx==fy)continue; merg(root[fx],root[fy],1,n); f[fy]=fx; } } } int main() { init(); work(); return 0; }
本文来自博客园,作者:Glowingfire,转载请注明原文链接:https://www.cnblogs.com/Glowingfire/p/18576099
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程