曾经在森林里,有 N 只好斗的猴子。一开始,他们各自以自己的方式做事,彼此之间都不认识。但是猴子无法避免争吵,争吵只发生在两只彼此不认识的猴子之间。当发生这种情况时,两只猴子都将邀请他们中最坚强的朋友并进行决斗。当然,在决斗之后,两只猴子和那里所有的朋友彼此认识,即使这些猴子曾经发生过冲突,上面的争吵也不会再发生。
假设每只猴子都有一个强力值,在决斗后,该值将只减少为原始值的一半(即 10 减少为 5 , 5 减少为 2 )。
我们还假设每只猴子都认识自己。也就是说,当他是所有朋友中最坚强的人时,他本人将进行决斗。
分析
给每个猴子建一棵大根堆左偏树,当两个猴子成为朋友的时候,就把它们所在的左偏树合并,而找出最坚强的朋友,就返回它所在的左偏树的堆顶。减少朋友的强力值的话,先删除朋友,将权值减半再将该点作为一棵左偏树合并进原来的左偏树中就好了。
代码
//=========================
// author:hliwen
// date:2020.1.18
//=========================
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 100005
#define il inline
#define re register
#define DEBUG puts("ok")
#define tie0 cin.tie(0),cout.tie(0)
#define fastio ios::sync_with_stdio(false)
#define File(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout)
using namespace std;
typedef long long ll;
template <typename T> inline void read(T &x) {
T f = 1; x = 0; char c;
for (c = getchar(); !isdigit(c); c = getchar()) if (c == '-') f = -1;
for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
x *= f;
}
int n, m;
int val[N], fa[N], dis[N], rs[N], ls[N];
int merge(int x, int y) {
if (!x || !y) return x + y;
if (val[x] < val[y]) swap(x, y);
rs[x] = merge(rs[x], y);
fa[rs[x]] = x;
if (dis[ls[x]] < dis[rs[x]]) swap(ls[x], rs[x]);
dis[x] = dis[rs[x]] + 1;
return x;
}
int pop(int x) {
val[x] >>= 1;
int y = fa[ls[x]] = fa[rs[x]] = merge(ls[x], rs[x]);
ls[x] = rs[x] = 0;
return fa[x] = fa[y] = merge(x, y);
}
int find(int x) {
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
int main() {
int x, y, ans;
while (~scanf("%d", &n)) {
for (int i = 1; i <= n; ++i) {
read(val[i]);
fa[i] = i;
dis[i] = rs[i] = ls[i] = 0;
}
read(m);
for (int i = 1; i <= m; ++i) {
read(x), read(y);
if (find(x) == find(y)) puts("-1");
else {
int fx = find(x), fy = find(y);
ans = merge(pop(fx), pop(fy));
printf("%d\n", val[ans]);
}
}
}
return 0;
}