[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

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

Sample Output

-1
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 }

 

posted @ 2018-01-10 18:49  kakakakakaka  阅读(159)  评论(0编辑  收藏  举报

Never forget why you start

//鼠标爆炸特效