BZOJ2733: [HNOI2012]永无乡
2733: [HNOI2012]永无乡
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 4426 Solved: 2363
[Submit][Status][Discuss]
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
【题解】
不难看出启发式合并。
我写完之后突然发现。。。。尼玛我TM怎么全程没有用过splay?
丁队早期的板子有点不靠谱啊。。。
我查了查才知道。。。
insert后需要splay(本题不行,除非执行删除节点操作)
getk需要splay
...
反正啥啥都得splay一下。。
还好现在发现了,不然命丧IOI2019(???)
1 /************************************************************** 2 Problem: 2733 3 User: 33511595 4 Language: C++ 5 Result: Accepted 6 Time:3168 ms 7 Memory:4808 kb 8 ****************************************************************/ 9 10 #include <iostream> 11 #include <cstdio> 12 #include <cstring> 13 #include <cstdlib> 14 #include <algorithm> 15 #include <queue> 16 #include <vector> 17 #include <map> 18 #include <string> 19 #include <cmath> 20 #define min(a, b) ((a) < (b) ? (a) : (b)) 21 #define max(a, b) ((a) > (b) ? (a) : (b)) 22 #define abs(a) ((a) < 0 ? (-1 * (a)) : (a)) 23 template <class T> 24 inline void swap(T& a, T& b) 25 { 26 T tmp = a;a = b;b = tmp; 27 } 28 inline void read(int &x) 29 { 30 x = 0;char ch = getchar(), c = ch; 31 while(ch < '0' || ch > '9') c = ch, ch = getchar(); 32 while(ch <= '9' && ch >= '0') x = x * 10 + ch - '0', ch = getchar(); 33 if(c == '-') x = -x; 34 } 35 36 const int INF = 0x3f3f3f3f; 37 const int MAXN = 100000 + 10; 38 int fa[MAXN], ch[MAXN][2], size[MAXN], cnt[MAXN], data[MAXN], node[MAXN], n, nn, root[MAXN], father[MAXN]; 39 int find(int x) 40 { 41 return x == father[x] ? x : father[x] = find(father[x]); 42 } 43 int son(int x){return x == ch[fa[x]][1];} 44 void pushup(int x) 45 { 46 size[x] = size[ch[x][0]] + size[ch[x][1]] + cnt[x]; 47 } 48 49 //编号为k的平衡树 50 void rotate(int x, int k) 51 { 52 int y = fa[x], z = fa[y], b = son(x), c = son(y), a = ch[x][!b]; 53 if(z) ch[z][c] = x; else root[k] = x; fa[x] = z; 54 if(a) fa[a] = y; ch[y][b] = a; 55 ch[x][!b] = y, fa[y] = x; 56 pushup(y), pushup(x); 57 } 58 59 //编号为k的平衡树 60 void splay(int x, int i, int k) 61 { 62 while(fa[x] != i) 63 { 64 int y = fa[x], z = fa[y]; 65 if(z == i) rotate(x, k); 66 else 67 if(son(x) == son(y)) rotate(y, k), rotate(x, k); 68 else rotate(x, k), rotate(x, k); 69 } 70 } 71 72 //编号为k的平衡树 73 int getmn(int rt, int k) 74 { 75 int ans = -1; 76 while(rt) 77 { 78 ans = rt; 79 rt = ch[rt][0]; 80 } 81 if(ans != -1) splay(ans, 0, k); 82 return ans; 83 } 84 85 //编号为k的平衡树 86 int getkth(int rt, int kk, int k) 87 { 88 int l = ch[rt][0]; 89 if(size[l] + 1 <= kk && kk <= size[l] + cnt[rt]) 90 { 91 splay(rt, 0, k); 92 return rt; 93 } 94 else if(size[l] + 1 > kk) return getkth(l, kk, k); 95 else return getkth(ch[rt][1], kk - size[l] - cnt[rt], k); 96 } 97 98 //插入节点zz 99 void insert(int &rt, int zz) 100 { 101 if(rt == 0) 102 { 103 rt = zz, size[zz] = 1; 104 return; 105 } 106 if(data[zz] < data[rt]) insert(ch[rt][0], zz), fa[ch[rt][0]] = rt; 107 else if(data[zz] > data[rt]) insert(ch[rt][1], zz), fa[ch[rt][1]] = rt; 108 pushup(rt); 109 } 110 111 //把以x节点为根的树插入到以y节点为根的树上 112 void dfs(int x, int y, int k) 113 { 114 if(!x) return; 115 int tmp = y; 116 insert(tmp, x); 117 dfs(ch[x][0], y, k); 118 dfs(ch[x][1], y, k); 119 } 120 121 //把编号为y的岛与编号为x的岛连接,y插入到x 122 void lk(int x, int y) 123 { 124 int f1 = find(x), f2 = find(y); 125 if(f1 == f2) return; 126 if(size[root[f1]] < size[root[f2]]) swap(f1, f2); 127 dfs(root[f2], root[f1], f1); 128 father[f2] = f1; 129 } 130 131 //查询与x联通的岛中第y重要的岛 132 void ask(int x, int y) 133 { 134 int t = find(x), tmp = root[t]; 135 if(size[tmp] < y) printf("-1\n"); 136 else printf("%d\n", node[getkth(tmp, y, t)]); 137 } 138 139 int m,q,tmp1,tmp2; 140 char c; 141 142 int main() 143 { 144 read(n);read(m); 145 for(int i = 1;i <= n;++ i) read(tmp1), father[i] = i, root[i] = i, data[i] = tmp1, node[i] = i, cnt[i] = 1, ++ nn, size[i] = 1; 146 for(int i = 1;i <= m;++ i) read(tmp1), read(tmp2), lk(tmp1, tmp2); 147 read(q); 148 for(int i = 1;i <= q;++ i) 149 { 150 scanf("%s", &c), read(tmp1), read(tmp2); 151 if(c == 'Q') ask(tmp1, tmp2); 152 else lk(tmp1, tmp2); 153 } 154 return 0; 155 }