ZOJ 2334 Monkey King(左偏树)

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2334

题意:N个猴子,打了M次架,每次打架的时候双方猴子都会派出自己一方战斗力最高的猴子,打完架后这两只猴子的战斗力都减半。同时,不打不相识嘛,两方猴子打完架后就互相认识变成同伙不会再打架。输出每次打架后通过这次冲突合并的猴子中战斗力最高的猴子的战斗力值,如果冲突双方在同一伙的话直接输出-1。

思路:对于判断两个猴子是不是在一个里面直接用并查集就好。然后,两个群体的合并直接用左偏树的merge就好。这里显然是根节点存储最大值。那么当询问两个猴子时,我们分别找到其所在群里中最牛逼的猴子,也就是在其所在子树的根节点的那个猴子,将其减半后删掉再插入,最后将两个子树合并,输出根节点的值就好。在这里,怎么找到其所在子树的根节点呢?一开始我加了个parent指针每次向上找,其实每次合并时用并查集中的根节点的指针指向合并后树的根节点就好,就不用每次沿着parent向上找了,只用并查集找到的就行。

 



struct node
{
    int val,Max;
    int dis;
    node *c[2];
};


node a[N],*root[N],*nullNode;
int cnt;


node *newNode(int val)
{
    node *e=&a[cnt++];
    e->c[0]=e->c[1]=nullNode;
    e->Max=e->val=val;
    e->dis=0;
    return e;
}


void init()
{
    cnt=0;
    nullNode=0;
    nullNode=newNode(0);
    nullNode->dis=-1;
    nullNode->Max=-INF;
}


void pushUp(node *x)
{
    if(x==nullNode) return;
    x->Max=max(x->val,max(x->c[0]->Max,x->c[1]->Max));
    x->dis=x->c[1]->dis+1;
}


node *merge(node *a,node *b)
{
    if(a==nullNode) return b;
    if(b==nullNode) return a;
    if(a->val<b->val) swap(a,b);
    a->c[1]=merge(a->c[1],b);
    if(a->c[0]->dis<a->c[1]->dis) swap(a->c[0],a->c[1]);
    pushUp(a);
    return a;
}


node *del(node *&a)
{
    return merge(a->c[0],a->c[1]);
}


int S[N],n,m,s[N];


int find(int x)
{
    if(s[x]!=x) s[x]=find(s[x]);
    return s[x];
}


void solve(int x,int y)
{
    int xx=find(x),yy=find(y);
    if(xx==yy)
    {
        puts("-1");
        return;
    }
    s[xx]=yy;
    node *p1=newNode(root[xx]->Max>>1);
    node *p2=newNode(root[yy]->Max>>1);
    node *p3=del(root[xx]);
    node *p4=del(root[yy]);
    p3=merge(p1,p3);
    p4=merge(p2,p4);
    root[yy]=merge(p3,p4);
    PR(root[yy]->Max);
}


int main()
{
    Rush(n)
    {
        int i;
        FOR1(i,n) RD(S[i]);
        init();
        FOR1(i,n) root[i]=newNode(S[i]);
        FOR1(i,n) s[i]=i;
        RD(m);
        int x,y;
        while(m--)
        {
            RD(x,y);
            solve(x,y);
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值