[HNOI2012]永无乡
Description
永无乡包含 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
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
2
5
1
2
题解:
1.一道Splay的裸题,用并查集辅助判断一下是否在同一个集合中。
2.初始每一个点都是一棵Splay,维护树中k大值是Splay的正常操作。
3.每次并的时候用启发式合并,所以在Splay中要保存size参数表示字数的大小,然后每次将小的Splay放到大的Splay中。
1 //Never forget why you start 2 #include<iostream> 3 #include<cstdio> 4 #include<cstdlib> 5 #include<cstring> 6 #include<cmath> 7 #include<algorithm> 8 #define ll(x) (bst[x].child[0]) 9 #define rr(x) (bst[x].child[1]) 10 #define son(x,t) (bst[x].child[t]) 11 using namespace std; 12 int n,m; 13 struct Fa { 14 int fa[100005],cnt; 15 void clean() { 16 for(int i=1; i<=n; i++)fa[i]=i; 17 cnt=n; 18 } 19 int find(int x) { 20 if(fa[x]==x)return x; 21 else return fa[x]=find(fa[x]); 22 } 23 void merge(int x,int y) { 24 int p=find(x),q=find(y); 25 if(p!=q) { 26 fa[p]=q; 27 cnt--; 28 } 29 } 30 } fa;//并查集辅助判断是否联通 31 struct node { 32 int fa,child[2],x,size; 33 } bst[100005];//Splay结构体 34 void push_up(int r) { 35 bst[r].size=bst[son(r,0)].size+bst[son(r,1)].size+1; 36 }//统计size 37 void rotate(int r,int t) { 38 int fa=bst[r].fa; 39 son(fa,!t)=son(r,t);bst[son(r,t)].fa=fa; 40 son(bst[fa].fa,son(bst[fa].fa,1)==fa)=r;bst[r].fa=bst[fa].fa; 41 son(r,t)=fa;bst[fa].fa=r; 42 push_up(fa);push_up(r);//每次旋转都会使得大小发生变化,所以要重新更新 43 //注意这两个push_up不能写反 44 } 45 void Splay(int r,int goal) { 46 int fa=bst[r].fa; 47 while(fa!=goal) { 48 if(bst[fa].fa==goal)rotate(r,son(fa,0)==r); 49 else { 50 int t=son(bst[fa].fa,0)==fa; 51 if(son(fa,t)==r)rotate(r,!t),rotate(r,t); 52 else rotate(fa,t),rotate(r,t); 53 } 54 fa=bst[r].fa; 55 } 56 }//Splay树骚操作 57 int r; 58 void insert(int x,int root) { 59 while(1) { 60 bst[root].size++; 61 if(bst[x].x>bst[root].x) { 62 if(!son(root,1)) { 63 son(root,1)=x; 64 bst[x].fa=root; 65 Splay(x,0); 66 return; 67 } else root=son(root,1); 68 } else { 69 if(!son(root,0)) { 70 son(root,0)=x; 71 bst[x].fa=root; 72 Splay(x,0); 73 return; 74 } else root=son(root,0); 75 } 76 } 77 }//向Splay中插入一个点 78 void trash(int x,int y) { 79 if(!x)return; 80 trash(ll(x),y);trash(rr(x),y); 81 son(x,1)=son(x,0)=bst[x].size=bst[x].fa=0; 82 insert(x,r); 83 r=x; 84 }//将一棵Splay的每一个点分别插入到另一棵Splay 85 void merge(int u,int v) { 86 if(u==v)return; 87 Splay(u,0);Splay(v,0); 88 if(bst[u].size>bst[v].size)swap(u,v); 89 r=v; 90 trash(u,r); 91 }//启发式合并两棵Splay 92 int k_th(int a,int b) { 93 if(bst[a].size<b)return -1; 94 if(bst[son(a,0)].size==b-1)return a; 95 if(bst[son(a,0)].size>b-1)return k_th(son(a,0),b); 96 if(bst[son(a,0)].size<b-1)return k_th(son(a,1),b-bst[son(a,0)].size-1); 97 }//查找k大值 98 int main() { 99 int i,j; 100 scanf("%d%d",&n,&m); 101 fa.clean(); 102 for(i=1; i<=n; i++) { 103 scanf("%d",&j); 104 bst[i].size=1; 105 bst[i].x=j; 106 } 107 for(i=1; i<=m; i++) { 108 int u,v; 109 scanf("%d%d",&u,&v); 110 merge(fa.find(u),fa.find(v)); 111 fa.merge(u,v); 112 } 113 scanf("%d",&m); 114 for(i=1; i<=m; i++) { 115 char s[2]; 116 int a,b; 117 scanf("%s",s); 118 if(s[0]=='B') { 119 scanf("%d%d",&a,&b); 120 merge(fa.find(a),fa.find(b)); 121 fa.merge(a,b); 122 } else { 123 scanf("%d%d",&a,&b); 124 Splay(a,0); 125 printf("%d\n",k_th(a,b)); 126 } 127 } 128 return 0; 129 }