您的位置:首页 > 其它

HDU 1512 Monkey King 左偏树 + 并查集

2016-07-12 14:54 337 查看
题目:http://acm.hdu.edu.cn/showproblem.php?pid=1512

题意:有n个猴子,一开始每个猴子只认识自己。每个猴子有一个力量值,力量值越大表示这个猴子打架越厉害。然后给出两个数字,代表两只猴子有矛盾要决斗,如果2个猴子不认识,他们就会找他们认识的猴子中力量最大的出来单挑,单挑不论输赢,单挑的2个猴子力量值减半,这2拨猴子就都认识了。现在给m组询问,如果2只猴子相互认识,输出-1,否则他们各自找自己认识的最牛叉的猴子单挑,求挑完后这拨猴子力量最大值。

思路:学习左偏树。。。这篇很好左偏树的特点及其应用  ,维护每个猴子和他的朋友可以用并查集,然后维护他们之中最强的猴子则要用类似优先队列的数据结构,两拨猴子认识后就成了一拨,并查集要合并,维护战斗力的数据结构也要合并,用优先队列不能实现迅速合并,而左偏树就可以迅速的实现,因此用左偏树 + 并查集

#include <bits/stdc++.h>

using namespace std;

const int N = 100100;
struct node
{
int l, r, d, val; //l,r是左右儿子,d维护左偏树距离
void init(int _val)
{
val = _val;
l = r = d = 0;
}
}tr
;
int par
, rnk
;
void init(int n)
{
for(int i = 1; i <= n; i++) par[i] = i;
}
int ser(int x)
{
int r = x, i = x, j;
while(r != par[r]) r = par[r];
while(i != r) j = par[i], par[i] = r, i = j;
return r;
}
void unite(int x, int y)
{
x = ser(x), y = ser(y);
if(x == y) return;
par[x] = y;
}
int leftree_merge(int x, int y) //合并两个左偏树
{
if(! x) return y; //有一个为空,则返回另一个
if(! y) return x;
if(tr[x].val < tr[y].val) swap(x, y);
tr[x].r = leftree_merge(tr[x].r, y);
par[tr[x].r] = x;
if(tr[tr[x].l].d < tr[tr[x].r].d) swap(tr[x].l, tr[x].r); //距离大的作为左儿子,小的作为右儿子
if(tr[x].r) tr[x].d = tr[tr[x].r].d + 1; //更新距离
else tr[x].d = 0;
return x;
}
int leftree_pop(int x)
{
int ls = tr[x].l, rs = tr[x].r;
par[ls] = ls, par[rs] = rs;
tr[x].init(tr[x].val);
return leftree_merge(ls, rs);
}
int del(int x) //这道题中每次弹出的必然是根节点,所以不会破坏左偏树性质,因此不用维护距离,于是leftree_pop和del函数作用相同
{
int rx = par[x];
int ls = tr[x].l, rs = tr[x].r;
par[ls] = ls, par[rs] = rs;
//tr[x].l = tr[x].r = tr[x].d = 0;
tr[x].init(tr[x].val);
int p = leftree_merge(ls, rs); //把待删除节点的两个儿子合并
if(rx != x) //把新合并的点挂到待删除节点的父亲下面
{
if(tr[rx].l == x) tr[rx].l = p;
if(tr[rx].r == x) tr[rx].r = p;
}
x = rx;
while(x != par[x]) //删除一个点后可能会破坏左偏树性质,则从下至上更新距离
{
if(tr[tr[x].l].d < tr[tr[x].r].d) swap(tr[x].l, tr[x].r);
if(tr[tr[x].r].d + 1 == tr[x].d) break;
tr[x].d = tr[tr[x].r].d + 1;
rx = par[x];
}
return p; //返回其左右儿子合并后的节点
}
int main()
{
int n, m, val;
while(~ scanf("%d", &n))
{
init(n);
for(int i = 1; i <= n; i++)
{
scanf("%d", &val);
tr[i].init(val);
}
scanf("%d", &m);
int v, u;
for(int i = 1; i <= m; i++)
{
scanf("%d%d", &v, &u);
int rv = ser(v), ru = ser(u);
if(rv == ru) puts("-1");
else
{
int rrv = leftree_pop(rv);
//int rrv = del(rv);
tr[rv].val /= 2;
rv = leftree_merge(rrv, rv);
int rru = leftree_pop(ru);
//int rru = del(ru);
tr[ru].val /= 2;
ru = leftree_merge(rru, ru);
printf("%d\n", tr[leftree_merge(rv, ru)].val);
}
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: