标签:左偏树&并查集

思路来源

Q1:为什么用并查集?
A1:因为要判断两队猴子是否认识,在此处使用并查集可以便于维护两队猴子间的关系。

Q2:为什么用堆?
A2:因为每次对战都需要查找当前两队猴子中能力值最大的猴子,这里使用堆可以便于选出首领。

Q3:用什么样的堆?
A3:因为需要在两队猴子争执之后将两队猴子合并,并且不能破坏堆,所以要使用可并堆。

Q4:用什么可并堆?
A4:左偏树,斐波那契堆都是不错的可并堆,这里我使用的是左偏树。

具体做法

  • 使用并查集维护两队猴子之间的关系,在争执前先特判两队猴子已经认识的情况。
  • 根据题目要求,用左偏树模拟合并过程即可。

参考代码

#include<iostream>
#include<cstdio>
using namespace std;

struct monkey {
    int lson;//用下标模拟地址 
    int rson;//用下标模拟地址 
    int power;//能力值 
    int dist;//节点到离它最近的外节点的距离(备注:外节点为叶节点或只有左(右)子树的节点) 
}m[100005];

int f[100005];

int ff(int ele) {//并查集-路径压缩 
    if (ele==f[ele]) {
        return ele;
    }
    return f[ele]=ff(f[ele]);
}

int merge(int ele1,int ele2) {//合并两个左偏树,并返回新书的根节点 
    if (ele1==0) {//如果有一棵待合并的树为空树,则合并结果为另一棵树 
        return ele2;
    }
    if (ele2==0) {//同上 
        return ele1;
    }
    if (m[ele1].power<m[ele2].power) {//保证第一棵树的能力值<第二颗树 
        swap(ele1,ele2);
    }
    m[ele1].rson=merge(m[ele1].rson,ele2);//将较小的树合并到较大树的右子树中 
    f[m[ele1].rson]=ele1;
    if (m[ele1].rson==0) {//更新根节点到离它最近的外节点的距离 
        m[ele1].dist=0;
    }
    else {
        m[ele1].dist=m[m[ele1].rson].dist+1;
    }
    if (m[m[ele1].lson].dist<m[m[ele1].rson].dist) {//根据左偏树的性质,当左侧的长度>右侧的长度时,要交换左右子树 
        swap(m[ele1].lson,m[ele1].rson);
    }
    return ele1;
}

int main() {
    int n,t,x,y,p,q,root,newx,newy;
    while (scanf("%d",&n)!=EOF) {
        for (register int i=1;i<=n;++i) {
            scanf("%d",&m[i].power);
        }
        for (register int i=1;i<=n;++i) {//在最初的时候,每只猴子独立形成一个群体 
            m[i].lson=0;
            m[i].rson=0;
            f[i]=i;
        }
        scanf("%d",&t);
        while (t--) {
            scanf("%d%d",&x,&y);
            p=ff(x);//找出各自的首领,这部分用并查集维护 
            q=ff(y);
            if (p==q) {//按题目要求,两只本来就认识的猴子不会发生争执 
                printf("-1\n");
                continue;
            }
            m[p].power/=2;//更新首领在争执后剩余的能力值 
            root=merge(m[p].lson,m[p].rson);//先合并原首领的左右子树 
            m[p].lson=0;//将首领孤立 
            m[p].rson=0;
            newx=merge(root,p);//让首领归队,这部分用左边数维护,以便选出首领 
            m[q].power/=2;//同上 
            root=merge(m[q].lson,m[q].rson);
            m[q].lson=0;
            m[q].rson=0;
            newy=merge(root,q);
            root=merge(newx,newy);//将争执后的两队猴子合并 
            f[newx]=root;//更新并查集 
            f[newy]=root;
            printf("%d\n",m[root].power);//输出新首领的能力值 
        }
    }
    return 0;
}