【左偏树+并查集】Monkey King HDU1512

Monkey King

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 2320    Accepted Submission(s): 975



Problem Description
Once in a forest, there lived N aggressive monkeys. At the beginning, they each does things in its own way and none of them knows each other. But monkeys can't avoid quarrelling, and it only happens between two monkeys who does not know each other. And when it happens, both the two monkeys will invite the strongest friend of them, and duel. Of course, after the duel, the two monkeys and all of there friends knows each other, and the quarrel above will no longer happens between these monkeys even if they have ever conflicted.

Assume that every money has a strongness value, which will be reduced to only half of the original after a duel(that is, 10 will be reduced to 5 and 5 will be reduced to 2).

And we also assume that every monkey knows himself. That is, when he is the strongest one in all of his friends, he himself will go to duel.
 

Input
There are several test cases, and each case consists of two parts.

First part: The first line contains an integer N(N<=100,000), which indicates the number of monkeys. And then N lines follows. There is one number on each line, indicating the strongness value of ith monkey(<=32768).

Second part: The first line contains an integer M(M<=100,000), which indicates there are M conflicts happened. And then M lines follows, each line of which contains two integers x and y, indicating that there is a conflict between the Xth monkey and Yth.

 

Output
For each of the conflict, output -1 if the two monkeys know each other, otherwise output the strongness value of the strongest monkey in all friends of them after the duel.
 

Sample Input
  
  
5 20 16 10 10 4 5 2 3 3 4 3 5 4 5 1 5
 

Sample Output
  
  
8 5 5 -1 10
 

Author
JIANG, Yanyan
 

Source
 

Recommend
linle
 




题目大意:

有N个猴子,每个猴子一开始互相不认识,然后每次可以有a和b两只猴子发起决斗,在 a 的所有朋友中(包括a)和 b 的所有朋友中(包括b)战斗力最强的出场,每次决斗后的两个人战斗力减半(向下取整),然后决斗后的两个人就是朋友了(他们分别的朋友也互相成为朋友),求出每次决斗后他们中战斗力最强的战斗力值,朋友之间不会再次决斗,所以如果要决斗的a和b本身就是朋友,输出 -1



很明显,判断是不是朋友可以用并查集来实现,不解释。。。。

然后每次需要找出集合中最大的那个值,可以用堆来实现,然后并查集合并过后我们同样需要更新整个堆,怎么更新呢?枚举?O(N)?太慢了!!!

所以就需要一个可合并的推(可并堆),现在只会左偏树(刚学的,。。。。。)


这个应该是模板题了。。。。



测评情况(HDU)

先写了一次A了,为了熟悉代码又写了一次,很不幸CE了,,,,改了过后A



C++ AC Code

/*http://blog.csdn.net/jiangzh7
By Jiangzh*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using std::swap;
const int N=100000+10;
int n;
int A[N],L[N],R[N],D[N];
int f[N];

int getroot(int x){return f[x]==x ? x : f[x]=getroot(f[x]);}

int merge(int x,int y)//合并x y,其实相当于把y插入x
{
	if(!x||!y) return x|y;
	if(A[x]<A[y]) swap(x,y);//维护大根堆性质
	R[x]=merge(R[x],y);
	f[R[x]]=x;//更新并查集
	if(D[R[x]]>D[L[x]]) swap(L[x],R[x]);//保持左偏树性质
	D[x]=D[R[x]]+1;//更新左偏树信息
	return x;//返回合并后的根
}

int getmax(int x)//取出最大值并删除
{
	f[L[x]]=L[x];f[R[x]]=R[x];//更新并查集
	int t=merge(L[x],R[x]);
	L[x]=R[x]=D[x]=0;//删除
	return t;//返回删除后新的根
}

int fight(int x,int y)
{
	A[x]/=2; A[y]/=2;
	int xx=getmax(x);//先把最大值取出
	int yy=getmax(y);
	xx=merge(xx,x);//再合并进去
	yy=merge(yy,y);
	int tmp=merge(xx,yy);//两个堆合并
	return A[tmp];
}

int work(int x,int y)
{
	int fx=getroot(x);//并查集得到祖先
	int fy=getroot(y);
	if(fx==fy) return -1;
	return fight(fx,fy);
}

int main()
{
	freopen("monkey.in","r",stdin);
	freopen("monkey.out","w",stdout);
	while(scanf("%d",&n)==1)
	{
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&A[i]);
			L[i]=R[i]=D[i]=0;
			f[i]=i;
		}
		int q;scanf("%d",&q);
		while(q--)
		{
			int a,b;
			scanf("%d%d",&a,&b);
			printf("%d\n",work(a,b));
		}
	}
	return 0;
}





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值