• [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
    ",k_th(a,b));
    126         }    
    127     }
    128     return 0;
    129 }
  • 相关阅读:
    函数
    特殊集合
    集合
    数组复习
    数组
    IPython--转
    python 单例模式总结
    拼多多笔试题
    python 创建实例--待完善
    转--算法时间复杂度
  • 原文地址:https://www.cnblogs.com/huangdalaofighting/p/8260245.html
Copyright © 2020-2023  润新知