HDU 1512 Monkey King(左偏树模板题)
http://acm.hdu.edu.cn/showproblem.php?pid=1512
题意:
有n只猴子,每只猴子一开始有个力量值,并且互相不认识,现有每次有两只猴子要决斗,如果认识,就不打了,否则的话,这两只都会从它们所认识的猴子中派出一只力量值最大的猴子出来,并且这只猴子的力量值会减半,在打过之后,这两只猴子所在的集体就都认识了。
思路:
这是一道左偏树的模板题。
每个节点有5个值,l为左儿子节点,r为右儿子节点,fa为父亲节点(父亲节点的用法与并查集相似),val为优先级(这道题目就是大根堆),dis是距离值(在两棵树合并的时候使用,根据dis来决定左右子树是否需要对换)。
一开始每只猴子就是一棵树,每次决斗就是将两棵树进行合并。
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 const int maxn = 100000+5; 5 6 int n, m; 7 8 struct Heap 9 { 10 int l,r,fa,val,dis; 11 }t[maxn]; 12 13 14 int finds(int x) 15 { 16 return t[x].fa == -1? x:t[x].fa = finds(t[x].fa); 17 } 18 19 int merge(int x, int y) 20 { 21 if(x == 0) return y; //如果为0的话,就说明是空子树,根节点当然就是另一节点了 22 if(y == 0) return x; 23 if(t[y].val>t[x].val) swap(x,y); //始终往右子树进行插入 24 t[x].r = merge(t[x].r,y); 25 t[t[x].r].fa = x; 26 if(t[t[x].l].dis < t[t[x].r].dis) swap(t[x].l,t[x].r); //是否需要左右子树的对换,这样是为了右子树尽量短 27 if(t[x].r == 0) t[x].dis = 0; //距离的重新分配 28 else t[x].dis = t[t[x].r].dis + 1; 29 return x; 30 } 31 32 int pop(int &root) 33 { 34 int l = t[root].l; 35 int r = t[root].r; 36 t[root].l = t[root].r = t[root].dis = 0; 37 t[root].fa = -1; 38 t[l].fa = t[r].fa = -1; //删除root根节点 39 return merge(l,r); //这样一来相当于分裂成了两棵子树,重新进行合并,最后返回值为合并后的根节点 40 } 41 42 int push(int x, int y) 43 { 44 return merge(x,y); 45 } 46 47 int main() 48 { 49 //freopen("in.txt","r",stdin); 50 while(~scanf("%d",&n)) 51 { 52 for(int i=1;i<=n;i++) 53 { 54 t[i].l=t[i].r=t[i].dis=0; 55 t[i].fa=-1; 56 scanf("%d",&t[i].val); 57 } 58 scanf("%d",&m); 59 while(m--) 60 { 61 int a,b; 62 scanf("%d%d",&a,&b); 63 int x=finds(a); 64 int y=finds(b); 65 if(x!=y) 66 { 67 t[x].val/=2; 68 int xx = push( pop(x),x); 69 t[y].val/=2; 70 int yy = push( pop(y),y); 71 printf("%d\n",t[merge(xx,yy)].val); 72 } 73 else puts("-1"); 74 } 75 } 76 return 0; 77 }