题目链接:HDU - 1512
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.
题意描述:有n只猴子,每只猴子有一个值,两只猴子如果打架的话,他们的值各自掉一半。给出m个事件,每个事件给出两只猴子,如果两只猴子不认识的话,打完架就成为了朋友(两只猴子的各自朋友也都互相成为了朋友),求出打完架后两只猴子的所有朋友中值最大的。
算法分析:这道题我刚开始做的时候,想到了并查集,以为这就够了(其实时间复杂度我也不敢直视),交上去果断TLE了,后来看了讨论里有说,尼玛,这就是传说中的左偏树的题目啊,果断得好好学习一下,弥补一下自己的数据结构的知识。
说明:头一次搞左偏树,代码是借鉴别人的,不过真心写的比较好就拿来了。同时,推荐一下集训队的论文,基本上看了论文后就对左偏树有了一定的了解了。
1 /*左偏树*/ 2 #include<iostream> 3 #include<cstdio> 4 #include<cstdlib> 5 #include<cstring> 6 #include<cmath> 7 #include<algorithm> 8 #define inf 0x7fffffff 9 using namespace std; 10 const int maxn = 100000+10; 11 12 int father[maxn]; 13 struct node 14 { 15 int l,r; 16 int dis; 17 int strong; 18 }LTree[maxn]; 19 int Find(int x) 20 { 21 if (father[x]==x) return x; 22 return father[x]=Find(father[x]); 23 } 24 int Merge(int x,int y) 25 { //返回合并后的根 26 if (x==0) return y; 27 if (y==0) return x; 28 if (LTree[x].strong < LTree[y].strong) //大顶堆 29 swap(x,y); 30 LTree[x].r = Merge(LTree[x].r,y); //递归合并右子树和Y 31 int l = LTree[x].l , r = LTree[x].r; 32 father[r] = x; //更新T右子树的根 33 if (LTree[l].dis < LTree[r].dis) //维护堆性质 34 swap(LTree[x].l,LTree[x].r); 35 if (LTree[x].r == 0) //如果没有右子树 则距离为0 36 LTree[x].dis = 0; 37 else 38 LTree[x].dis = LTree[LTree[x].r].dis + 1; 39 return x; 40 } 41 int del(int x) 42 { //返回删除根以后左右子树的合并的根 43 int l,r; 44 l=LTree[x].l; 45 r=LTree[x].r; 46 father[l]=l; 47 father[r]=r; 48 LTree[x].l=LTree[x].r=LTree[x].dis=0; 49 return Merge(l,r); 50 } 51 void solve(int x,int y) 52 { 53 LTree[x].strong /= 2; 54 LTree[y].strong /= 2; 55 //问每次PK以后,当前这个群体里力量最大的猴子的力量是多少。 56 int left,right; 57 left = del(x); 58 right = del(y); 59 left = Merge(left,x); 60 right = Merge(right,y); 61 left = Merge(left,right); 62 printf("%d\n",LTree[left].strong); 63 } 64 int main() 65 { 66 int n,m,x,y; 67 while (scanf("%d",&n)!=EOF) 68 { 69 for (int i=1 ;i<=n ;i++) 70 { 71 scanf("%d",<ree[i].strong); 72 LTree[i].l=0; 73 LTree[i].r=0; 74 LTree[i].dis=0; 75 father[i]=i; //起始已自己为父亲 76 } 77 scanf("%d",&m); 78 for (int i=1 ;i<=m ;i++) 79 { 80 scanf("%d%d",&x,&y); 81 int fx=Find(x),fy=Find(y); 82 if (fx == fy) printf("-1\n"); 83 else solve(fx,fy); 84 } 85 } 86 return 0; 87 }