hdu1512-Monkey King- 左偏树+并查集

http://acm.hdu.edu.cn/showproblem.php?pid=1512

题意:

有n个猴子,,一开始每个猴子只认识自己,每个都有自己的力量

 m个操作,每次给x y,表示x和y打架,

先判断x,y是否有同一个老大,如果是,输出-1,不打,

否则,让各自的老大(种群中力量值最大者) 打一架,后果是各自老大力量值减半,从此两个种群合并,仍旧是力量值最大的为新老大。输出新老大的力量值。

思路:

对于记录猴子的种群,就是用并查集,每次让猴子种群中力量最大的来打架,可以用优先队列,但问题是,每次有一个合并的操作,所以优先队列不合适,得用 左偏树,其插入,查询删除(顶点/最值点),都是logn,并且可以合并两棵树,复杂度也是logn,因此符合要求

具体实现见代码注释;


#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <set>
#include <vector>
using namespace std;   

const int maxn3=100010;

int a[maxn3];	//原始数据数组-可有可无
int c[maxn3];    //并查集数组
int tot,v[maxn3],l[maxn3],r[maxn3],d[maxn3];
//v为节点值,l,r为左右节点序号
//定义"外节点"为左或子树为空的节点,d指该点到其外节点最短距离(属于左偏树内容)
int merge(int x,int y)	//左偏树合并函数*最关键
{
    if (!x)
        return (y);
    if (!y)
        return (x);
    if (v[x]<v[y]) 
        swap(x,y);  
    r[x]=merge(r[x],y);
    if (d[l[x]]<d[r[x]]) 
        swap(l[x],r[x]);  
    d[x]=d[r[x]]+1;
    return x;
}
int  init(int x)  	//左偏树初始化新节点函数
{
    tot++;
    v[tot]=x;
    l[tot]=r[tot]=d[tot]=0;
    return tot;
}  
int top(int x)	//左偏树取顶点函数
{
    return v[x];
}
int pop(int x)		//左偏树pop顶点函数
{
    return merge(l[x],r[x]);
}

int find(int x)		//并查集查找函数
{
    if(x==c[x]) return x;
    return c[x]=find(c[x]);
}
int main()
{
	//    freopen( "1.out","r",stdin ); // scanf 从1.txt输入
	//     freopen( "test.out","w",stdout );  //printf输出到1.tx
    int n,m;
    int m1,m2,i;
	while( scanf("%d",&n)!=EOF)
	{ 
        tot=0; 
		
        
        for (i=1;i<=n;i++)
        {
            scanf("%d",&a[i]); 
            init(a[i]);		//初始化新节点
			c[i]=i;			//初始化并查集
        }
        cin>>m;
        for (i=1;i<=m;i++)
        {
            scanf("%d%d",&m1,&m2);
            int f1=find(m1);	//并查集查找根节点
            int f2=find(m2);
            if (f1==f2)	//老大相同即不打架
            {
                printf("-1\n");continue;
            }
            else
            { 
                int tt1=pop(f1);//去掉根并记录新节点
				int tt2=pop(f2); //去掉根并记录新节点
				v[f2]/=2;		//原根节点减半
				v[f1]/=2;		//原根节点减半
				l[f1]=r[f1]=d[f1]=0;	//把原根节点置零
				l[f2]=r[f2]=d[f2]=0;
                int yy=merge(tt1,tt2); //合并去掉根后的2个新树
                int zz=merge(yy,f1);	//新树插入两个减半后的原根节点
				zz=merge(zz,f2);		
				c[f1]=zz;//一开始树1都指向f1,现在直接指向最终新根节点即可
				c[f2]=zz;  
				c[zz]=zz;//保证最终根节点指向自己
                printf("%d\n",top(zz));//取出最终根节点
            }
            
        }
        
        
    }
    
    
    
    
    return 0;
    
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值