#左偏树,并查集#zoj 2334 hdu 1512 洛谷 1456 Monkey King

题目

有n只猴子,他们要打m次架,每次打架呢,都会拉上自己朋友最牛叉的出来跟别人打,打完之后战斗力就会减半,每次打完架就会成为朋友。问每次打完架之后那俩猴子最牛叉的朋友战斗力还有多少,若朋友打架就输出-1。


分析

二叉堆的合并比较慢,那么二项堆和FIBONACCI堆我都不会,所以可以用一种较中和的数据结构,左偏树,同时用并查集维护即可


代码

#include <cstdio>
#include <algorithm>
struct leftist_tree{int l,r,key,dis;}e[100001];
int n,m,f[100001];
int in(){
	int ans=0; char c=getchar();
	while (c<48||c>57) c=getchar();
	while (c>47&&c<58) ans=ans*10+c-48,c=getchar();
	return ans;
}
int getf(int u){
	int r=u,i=u,j;
	while (f[r]!=r) r=f[r];
	while (i!=r) j=f[i],f[i]=r,i=j;
	return r;
}
void print(int ans){if (ans>9) print(ans/10); putchar(ans%10+48);}
int merge(int x,int y){
	if (!x) return y;//没了
	if (!y) return x;//没了
	if (e[x].key<e[y].key) std::swap(x,y);//左偏,所以要交换
	e[x].r=merge(e[x].r,y);//用右子树合并
	int l=e[x].l,r=e[x].r; f[r]=x;
	if (e[l].dis<e[r].dis) std::swap(e[x].l,e[x].r);//the same
	e[x].dis=e[e[x].r].dis+1;//那么右边会有问题
	return x;
}
int del(int x){//删除节点
	int l=e[x].l; int r=e[x].r;
	f[l]=l; f[r]=r; 
	e[x]=(leftist_tree){0,0,e[x].key,0};
	return merge(l,r);//合并
}
void solve(int x,int y){
	e[x].key>>=1; e[y].key>>=1;
	int l=del(x); int r=del(y);//删除节点
	l=merge(l,x); r=merge(r,y);
	l=merge(l,r);//合并
	if (e[l].key) print(e[l].key); else putchar('0');
}
int main(){
	while (scanf("%d",&n)==1){
		e[0]=(leftist_tree){0,0,e[0].key,-1};
		for (int i=1;i<=n;i++)
			e[i]=(leftist_tree){0,0,in(),0},f[i]=i;
		m=in();
		while (m--){
			int x=getf(in()); int y=getf(in());
			if (x==y) putchar('-'),putchar('1'); else solve(x,y);
			putchar('\n');
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值