ZOJ2334 HDU1512 Monkey King,左偏树

60 篇文章 0 订阅
11 篇文章 0 订阅

学习了左偏树,发现及其强大,尤其是插入可以达到O(logN)的时间复杂度,我主要参考的是这些文章点击打开链接,又学到东西了

/*******************************************************************************
# Author : Neo Fung
# Email : neosfung@gmail.com
# Last modified: 2011-09-27 19:39
# Filename: ZOJ2334 HDU1512 Monkey King.cpp
# Description : 左偏树
******************************************************************************/
// #include "stdafx.h"
// #define DEBUG

#include <fstream>
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <string>
#include <memory.h>
#define MAX 100100

using namespace std;

struct NODE
{
	int r,l,val,dis;
}mon[MAX];
int pre[MAX];//用于保存祖先节点,其实这里可以把祖先节点保存在NODE中
int n,m;

void init(void)
{
	for(int i=0;i<=n;++i)
		pre[i]=i;
	memset(mon,'\0',sizeof(mon));
}
int inline find(int x)
{
	return x==pre[x]?x:pre[x]=find(pre[x]); //找到x的祖先节点
}

int merge(int a,int b)  //合并两个左偏树
{
	if(a==0) return b;
	if(b==0) return a;
	if(mon[a].val<mon[b].val)	//由于根节点的值比子孙节点的值大,而且这里是递归把右边的树和左边的树的右子树合并,所以要保证左边的树的根的值比右边的树的根的值大
		swap(a,b);
	mon[a].r=merge(mon[a].r,b);//把右边的树和左边的树的右子树合并

	pre[mon[a].r]=a;

	if( mon[mon[a].l].dis<mon[mon[a].r].dis)
		swap(mon[a].l, mon[a].r);// 保证右边的树的距离不大于左边的树的距离
	if(mon[a].r==0)
		mon[a].dis=0;
	else
		mon[a].dis=mon[mon[a].r].dis+1;
	return a;
}


int solve( int a, int b)
{
	int x=find(a),y=find(b);
	int temp;
	if(x==y) return -1;
	mon[x].val /=2;
	temp=merge(mon[x].l,mon[x].r);
	mon[x].l=mon[x].r=mon[x].dis=0;
	int xroot=merge(temp,x);

	mon[y].val /=2;
	temp = merge(mon[y].l , mon[y].r);
	mon[y].l=mon[y].r=mon[y].dis=0;
	int yroot=merge(temp , y);

	temp=merge(xroot,yroot);

	pre[x]=pre[y]=pre[xroot]=pre[yroot]=pre[a]=pre[b]=temp;

	return mon[temp].val;
}

int main(void)
{
#ifdef DEBUG  
	freopen("data.txt","r",stdin);  
#endif  
	int a,b;

	while(scanf("%d",&n)!=EOF)
	{
		init();
		for(int i=1;i<=n;++i)
			scanf("%d",&mon[i].val);
		scanf("%d",&m);
		for(int i=0;i<m;++i)
		{
			scanf("%d %d",&a,&b);
			printf("%d\n",solve(a,b));
		}
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值