[BZOJ2733] [HNOI2012] 永无乡 (splay启发式合并)

Posted CtrlCV

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[BZOJ2733] [HNOI2012] 永无乡 (splay启发式合并)相关的知识,希望对你有一定的参考价值。

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

HINT

Source

Solution

  维护多棵$splay$,当遇到合并操作时把节点个数少的那棵树所有节点,一个一个暴力插到另一棵树里

  整体来看,合并操作最坏情况下会进行$nlogn$次插入,所以合并的总复杂度是$O(nlog^2n)$,查询的总复杂度是$O(qlogn)$

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 struct spaly
  4 {
  5     int c[2], fa, siz, val;
  6     int& operator[] (int x)
  7     {
  8         return c[x];
  9     }
 10 }a[100005];
 11 int root[100005], fa[100005], sta[100005], top;
 12 
 13 int getfa(int x)
 14 {
 15     return fa[x] = x == fa[x] ? x : getfa(fa[x]);
 16 }
 17 
 18 void LDR(int u)
 19 {
 20     if(!u) return;
 21     LDR(a[u][0]), sta[++top] = u, LDR(a[u][1]);
 22 }
 23 
 24 void rotate(int &k, int x)
 25 {
 26     int y = a[x].fa, z = a[y].fa, dy = a[y][1] == x;
 27     if(k == y) k = x;
 28     else a[z][a[z][1] == y] = x;
 29     a[y][dy] = a[x][!dy], a[a[x][!dy]].fa = y;
 30     a[x][!dy] = y, a[y].fa = x, a[x].fa = z;
 31     a[y].siz = a[a[y][0]].siz + a[a[y][1]].siz + 1;
 32 }
 33 
 34 void splay(int &k, int x)
 35 {
 36     while(k != x)
 37     {
 38         int y = a[x].fa, z = a[y].fa;
 39         if(k != y)
 40             if(a[y][1] == x ^ a[z][1] == y) rotate(k, x);
 41             else rotate(k, y);
 42         rotate(k, x);
 43     }
 44     a[x].siz = a[a[x][0]].siz + a[a[x][1]].siz + 1;
 45 }
 46 
 47 void insert(int &k, int x)
 48 {
 49     if(!k)
 50     {
 51         k = x, a[x].siz = 1, a[x][0] = a[x][1] = 0;
 52         return;
 53     }
 54     ++a[k].siz;
 55     if(a[x].val < a[k].val)
 56         insert(a[k][0], x), a[a[k][0]].fa = k;
 57     else insert(a[k][1], x), a[a[k][1]].fa = k;
 58 }
 59 
 60 int find_kth(int k, int x)
 61 {
 62     if(!k) return -1;
 63     if(x <= a[a[k][0]].siz) return find_kth(a[k][0], x);
 64     if(x == a[a[k][0]].siz + 1) return k;
 65     return find_kth(a[k][1], x - a[a[k][0]].siz - 1);
 66 }
 67 
 68 void addedge(int u, int v)
 69 {
 70     if(u == v) return;
 71     if(a[root[u]].siz < a[root[v]].siz)
 72         swap(u, v);
 73     fa[v] = u, LDR(root[v]);
 74     while(top)
 75     {
 76         insert(root[u], sta[top]);
 77         splay(root[u], sta[top--]);
 78     }
 79 }
 80 
 81 int main()
 82 {
 83     int n, m, q, u, v;
 84     char op[5];
 85     scanf("%d%d", &n, &m);
 86     for(int i = 1; i <= n; ++i)
 87         scanf("%d", &a[i].val);
 88     for(int i = 1; i <= n; ++i)
 89         root[i] = fa[i] = i, a[i].siz = 1;
 90     while(m--)
 91     {
 92         scanf("%d%d", &u, &v);
 93         addedge(getfa(u), getfa(v));
 94     }
 95     scanf("%d", &q);
 96     while(q--)
 97     {
 98         scanf("%s%d%d", op, &u, &v);
 99         if(op[0] == \'B\') addedge(getfa(u), getfa(v));
100         else printf("%d\\n", find_kth(root[getfa(u)], v));
101     }
102     return 0;
103 }
View Code

 

以上是关于[BZOJ2733] [HNOI2012] 永无乡 (splay启发式合并)的主要内容,如果未能解决你的问题,请参考以下文章

bzoj2733: [HNOI2012]永无乡 启发式合并

[BZOJ] 2733: [HNOI2012]永无乡 #线段树合并+并查集

Bzoj 2733: [HNOI2012]永无乡 数组Splay+启发式合并

bzoj 2733: [HNOI2012]永无乡

BZOJ2733: [HNOI2012]永无乡

[bzoj2733] [HNOI2012]永无乡