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 

Sample Output 1

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\n", query(i, j));
    }
    return 0;
}
View Code
12-14 13:01