【HDU】1512 Monkey King 左偏树

8 篇文章 0 订阅
3 篇文章 0 订阅

题目传送门

题目大意:每次取出两个大根堆的对顶,将这两个节点的键值减半,维护堆的性质,然后将这两个大根堆合并起来,输出堆顶的键值。

很明显,这题的大部分时间都用来合并两个大根堆了,所以我们不能用普通的二叉堆,因为普通的二叉堆合并的时间复杂度是O(n)的。

考虑到合并的时间复杂度应该降低,我们就想到了左偏树(当然,斜堆、二项堆、斐波那契堆也是可以的),合并操作的时间复杂度是O(logn)的。

然后,就是一些简单的左偏树合并操作了。

附上AC代码:

#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define N 100010
using namespace std;

int n,w[N],m,f[N],ls[N],rs[N],dis[N],x,y;

bool read(int& a){
	static char c=getchar();a=0;int f=1;
	while (!isdigit(c)) {
		if (c==EOF) return 0;
		if (c=='-') f=-1;
		c=getchar();
	}
	while (isdigit(c)) a=a*10+c-'0',c=getchar();
	return 1;
}

int gf(int x){
	while (f[x]) x=f[x];
	return x;
}

int hb(int a,int b){
	if (a*b==0) return a+b;
	if (w[a]<w[b]) swap(a,b);
	rs[a]=hb(rs[a],b);
	f[rs[a]]=a;
	if (dis[ls[a]]<dis[rs[a]]) swap(ls[a],rs[a]);
	dis[a]=dis[ls[a]]+1;
	return a;
}

int main(void){
	while (read(n)){
		for (int i=1; i<=n; ++i) read(w[i]);
		memset(f,0,sizeof f),memset(ls,0,sizeof ls),memset(rs,0,sizeof rs),memset(dis,0,sizeof dis);
		read(m),dis[0]=-1;
		while (m--){
			read(x),read(y);
			x=gf(x),y=gf(y);
			if (x==y) puts("-1");
			else {
				f[ls[x]]=f[rs[x]]=f[ls[y]]=f[rs[y]]=0;
				int h=hb(hb(ls[x],rs[x]),hb(ls[y],rs[y]));
				ls[x]=rs[x]=0,w[x]>>=1,h=hb(h,x);
				ls[y]=rs[y]=0,w[y]>>=1,h=hb(h,y);
				printf("%d\n",w[h]);
			}
		}
	}
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值