cogs526 爱争吵的猴子

10 篇文章 0 订阅
4 篇文章 0 订阅

传送门

左偏树。
每次合并的时候取出堆顶,然后合并堆顶的左右儿子,最后将堆顶所代表的猴子的强壮值减半扔到左偏树里。
维护父亲的时候用并查集实现。
实现的时候有些细节需要注意。

CODE:

#include<cstdio>
#include<iostream>
using namespace std;
struct node
{
    int num,dis,id;
    node *ch[2];
}pool[300005],*t[300005],*null;
int f[300005];
int n,m,x,y,tot;
inline node *getnew(int value,int id)
{
    node *now=pool+ ++tot;
    now->ch[0]=now->ch[1]=null;
    now->num=value,now->id=id;
    now->dis=0;
    return t[tot]=now;
}
inline int find(int n)
{
    if(f[n]!=n) f[n]=find(f[n]);
    return f[n];
}
node *merge(node *x,node *y)
{
    if(x==null) return y;
    if(y==null) return x;
    if(x->num<y->num||(x->num==y->num&&x->id>y->id)) swap(x,y);
    x->ch[1]=merge(x->ch[1],y);
    if(x->ch[0]->dis<x->ch[1]->dis) swap(x->ch[0],x->ch[1]);
    x->dis=x->ch[1]->dis+1;
    return x;
}
inline int Merge(int x,int y)
{
    node *t1=t[x],*t2=t[y];
    int num1=t1->num,num2=t2->num;
    t1=merge(t1->ch[0],t1->ch[1]);
    t2=merge(t2->ch[0],t2->ch[1]);
    if(t1!=null)
    {
        f[t1->id]=t1->id;
        if(t1->ch[0]!=null) f[t1->ch[0]->id]=t1->id;
        if(t1->ch[1]!=null) f[t1->ch[1]->id]=t1->id;
    }
    if(t2!=null)
    {
        f[t2->id]=t2->id;
        if(t2->ch[0]!=null) f[t2->ch[0]->id]=t2->id;
        if(t2->ch[1]!=null) f[t2->ch[1]->id]=t2->id;
    }
    t1=merge(t1,getnew(num1>>1,tot+1)),f[tot]=t1->id;
    t2=merge(t2,getnew(num2>>1,tot+1)),f[tot]=t2->id;
    node *root=merge(t1,t2);
    f[x]=f[y]=f[t1->id]=f[t2->id]=root->id;
    return t[find(t1->id)]->num;
}
int main()
{
    freopen("monkeyk.in","r",stdin);
    freopen("monkeyk.out","w",stdout);
    null=pool;
    null->ch[0]=null->ch[1]=null;
    null->dis=-1;
    while(scanf("%d",&n)!=EOF)
    {
        tot=0;
        for(int i=1;i<=n;i++)
          scanf("%d",&x),getnew(x,i);
        scanf("%d",&m);
        for(int i=1;i<=n+m;i++)
          f[i]=i;
        while(m--)
        {
            scanf("%d%d",&x,&y);
            x=find(x),y=find(y);
            if(x!=y) printf("%d\n",Merge(x,y));
            else printf("-1\n");
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值