[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
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
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 }
以上是关于[BZOJ2733] [HNOI2012] 永无乡 (splay启发式合并)的主要内容,如果未能解决你的问题,请参考以下文章
[BZOJ] 2733: [HNOI2012]永无乡 #线段树合并+并查集