BZOJ2733: [HNOI2012]永无乡

2733: [HNOI2012]永无乡

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 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

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

 

【题解】

不难看出启发式合并。

我写完之后突然发现。。。。尼玛我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 }
BZOJ2733

 

posted @ 2018-01-29 08:28  嘒彼小星  阅读(201)  评论(0编辑  收藏  举报