P3224 [HNOI2012]永无乡
题目描述
永无乡包含 nn 座岛,编号从 11 到 nn ,每座岛都有自己的独一无二的重要度,按照重要度可以将这 nn 座岛排名,名次用 11 到 nn 来表示。某些岛之间由巨大的桥连接,通过桥可以从一个岛到达另一个岛。如果从岛 aa 出发经过若干座(含 00 座)桥可以 到达岛 bb ,则称岛 aa 和岛 bb 是连通的。
现在有两种操作:
B x y 表示在岛 xx 与岛 yy 之间修建一座新桥。
Q x k 表示询问当前与岛 xx 连通的所有岛中第 kk 重要的是哪座岛,即所有与岛 xx 连通的岛中重要度排名第 kk 小的岛是哪座,请你输出那个岛的编号。
输入输出格式
输入格式:
第一行是用空格隔开的两个正整数 nn 和 mm ,分别表示岛的个数以及一开始存在的桥数。
接下来的一行是用空格隔开的 nn 个数,依次描述从岛 11 到岛 nn 的重要度排名。随后的 mm 行每行是用空格隔开的两个正整数 a_iai 和 b_ibi ,表示一开始就存在一座连接岛 a_iai 和岛 b_ibi 的桥。
后面剩下的部分描述操作,该部分的第一行是一个正整数 qq ,表示一共有 qq 个操作,接下来的 qq 行依次描述每个操作,操作的 格式如上所述,以大写字母 QQ 或 BB 开始,后面跟两个不超过 nn 的正整数,字母与数字以及两个数字之间用空格隔开。
输出格式:
对于每个 Q x k 操作都要依次输出一行,其中包含一个整数,表示所询问岛屿的编号。如果该岛屿不存在,则输出 -1−1 。
输入输出样例
说明
对于 20% 的数据 n \leq 1000, q \leq 1000n≤1000,q≤1000
对于 100% 的数据 n \leq 100000, m \leq n, q \leq 300000n≤100000,m≤n,q≤300000
题解
这道题要求我们执行两个操作。1.判断是否在同一个集合 2.查找联通快第k小值
操作一我们可以用并查集维护,操作二的话,我们可以用splay维护
但合并怎么做呢?我看了看题解,发现有个叫做启发式合并的神器玩意儿
简而言之,就是合并两个splay时,把节点数较少的splay中每一个点都取出来插入另一个splay中(好暴力啊……暴力mo不可取……)
时间复杂度是O(n log n)的
原因?能用就好要什么原因
具体的实现请看代码
1 //minamoto 2 #include<iostream> 3 #include<cstdio> 4 #include<algorithm> 5 #include<cstring> 6 #define N 500050 7 using namespace std; 8 inline int read(){ 9 char ch;bool flag=0;int res; 10 while(!isdigit(ch=getchar())) 11 (ch=='-')&&(flag=true); 12 for(res=ch-'0';isdigit(ch=getchar());res=res*10+ch-'0'); 13 (flag)&&(res=-res); 14 return res; 15 } 16 int n,m; 17 struct node{ 18 int val,father,size,ch[2]; 19 } e[N]; 20 int cnt,root[N],f[N],h[N]; 21 int ff(int x){ 22 return f[x]==x?x:f[x]=ff(f[x]); 23 } 24 void pushup(int x){ 25 e[x].size=e[e[x].ch[0]].size+e[e[x].ch[1]].size+1; 26 } 27 int identify(int x){ 28 return e[e[x].father].ch[1]==x; 29 } 30 void connect(int x,int f,int son){ 31 e[x].father=f,e[f].ch[son]=x; 32 } 33 void rotate(int x){ 34 int y=e[x].father,z=e[y].father; 35 int yson=identify(x),zson=identify(y); 36 int b=e[x].ch[yson^1]; 37 connect(b,y,yson),connect(y,x,yson^1),connect(x,z,zson); 38 pushup(y),pushup(x); 39 } 40 void splay(int x,int goal){ 41 while(e[x].father!=goal){ 42 int y=e[x].father,z=e[y].father; 43 if(z!=goal) 44 (identify(x)^identify(y))?rotate(x):rotate(y); 45 rotate(x); 46 } 47 if(goal<=n) root[goal]=x; 48 } 49 void push(int x,int k){ 50 int u=root[k],fa=k; 51 while(u&&e[u].val!=x) 52 fa=u,u=e[u].ch[x>e[u].val]; 53 u=++cnt; 54 e[u].size=1,e[u].father=fa; 55 if(fa>n) 56 e[fa].ch[x>e[fa].val]=u; 57 e[u].val=x,e[u].ch[0]=e[u].ch[1]=0; 58 splay(u,k); 59 } 60 void dfs(int u,int k){ 61 if(e[u].ch[0]) dfs(e[u].ch[0],k); 62 if(e[u].ch[1]) dfs(e[u].ch[1],k); 63 push(e[u].val,k); 64 } 65 void merge(int a,int b){ 66 int x=ff(a),y=ff(b); 67 if(x==y) return; 68 if(e[root[x]].size>e[root[y]].size) swap(x,y); 69 f[x]=y; 70 dfs(root[x],y); 71 } 72 int get(int x,int k){ 73 int now=root[k]; 74 if(e[now].size<x) return -1; 75 while(true){ 76 if(e[e[now].ch[0]].size>=x) now=e[now].ch[0]; 77 else if(e[e[now].ch[0]].size+1==x) return e[now].val; 78 else x-=e[e[now].ch[0]].size+1,now=e[now].ch[1]; 79 } 80 } 81 int main(){ 82 //freopen("testdata.in","r",stdin); 83 n=read(),m=read(); 84 for(int i=1;i<=n;++i) root[i]=i+n,f[i]=i; 85 cnt=n+n; 86 for(int i=1;i<=n;++i){ 87 int x=read(); 88 h[x]=i; 89 e[i+n].val=x,e[i+n].size=1,e[i+n].father=i; 90 } 91 for(int i=1;i<=m;++i){ 92 int x=read(),y=read(); 93 merge(x,y); 94 } 95 int t=read(); 96 while(t--){ 97 char s[5];int a,b; 98 scanf("%s",s);a=read(),b=read(); 99 if(s[0]=='B') merge(a,b); 100 else{ 101 int ans=get(b,ff(a)); 102 printf("%d\n",~ans?h[ans]:ans); 103 } 104 } 105 return 0; 106 }