Description
有n个猴子,一开始每个猴子只认识自己。每个猴子有一个力量值,力量值越大表示这个猴子打架越厉害。如果2个猴子不认识,他们就会找他们认识的猴子中力量最大的出来单挑,单挑不论输赢,单挑的2个猴子力量值减半,这2拨猴子就都认识了,不打不相识嘛。现在给m组询问,如果2只猴子相互认识,输出-1,否则他们各自找自己认识的最牛叉的猴子单挑,求挑完后这拨猴子力量最大值。
Input
第一行为整数n,表示猴子数量,编号为1..n。 接下来的n行,每行一个整数,其中第i个整数表示编号为i的猴子的力量值(不超过10^9的正整数)。 再一行为整数m,表示询问数目。 接下来的m行,每行两个整数x,y(1<=x,y<=n),x,y为猴子的编号,表示一个询问,其意义如题目描述。
Output
对每个询问,输出其回答。
Sample Input 1
5 20 16 10 10 4 5 2 3 3 4 3 5 4 5 1 5
Sample Output 1
7
Hint
1<=n<=100000
用并查集判断,
要每次查询每个队的最大值,用堆维护,则要用可并堆,我用的斜堆。
合并时可以弹出新开节点再插入,也可以省空间将就原节点弹出再插入。
#include<bits/stdc++.h> using namespace std; const int maxn = 100005, maxm = 100005; int w[maxm]; int rt[maxn]; int lc[maxm], rc[maxm], np; int read() { int o=0, f=1; char ch=getchar(); while(!isdigit(ch)) { if(ch == '-') f=-1; ch=getchar(); } while(isdigit(ch)) o=o*10+ch-'0', ch=getchar(); return o*f; } int merge(int A, int B) { if(A == 0) return B; if(B == 0) return A; if(w[A] < w[B]) swap(A, B); rc[A] = merge(rc[A], B); swap(lc[A], rc[A]); return A; } int top(int A) { return w[A]; } int pop(int A) { return merge(lc[A], rc[A]); } int push(int A, int v) { w[++np] = v; return merge(A, np); } int fa[maxn]; int find(int x) { return fa[x] == x? x: fa[x] = find(fa[x]); } int reinsert(int A) { int nrt = pop(A); lc[A] = rc[A] = 0; w[A] /= 2; return merge(nrt, A); } int query(int x, int y) { int fx = find(x), fy = find(y); if(fx == fy) return -1; fa[fy] = fx; rt[fx] = reinsert(rt[fx]); rt[fy] = reinsert(rt[fy]); rt[fy] = rt[fx] = merge(rt[fx], rt[fy]); return top(rt[fx]); } int main() { int n=read(); for(int i=1; i<=n; i++) w[i] = read(), rt[i] = fa[i] = i; int m=read(); while(m--) { int i=read(), j=read(); printf("%d ", query(i, j)); } return 0; }