hdu1512 Monkey King(并查集,左偏堆)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1512

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

/*
每次给出要争吵的猴子a和b,用并查集判断如果他们是朋友输出-1
如果不是,找出a,b在的堆的根A,B,分别合并A,B的左右孩子,再合并一下。
之后把A,B的数据更改一下:权值除以2,左右孩子设为0,再插入到堆中即可。
最后输出堆顶。
*/
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 100005;
int m,n;
int set[N];
struct node
{
    int l,r,dis,key;
}tree[N];

int find(int x) {return set[x] == x ? x : set[x] = find(set[x]);}

int merge(int a,int b)
{
    if(!a)
        return b;
    if(!b)
        return a;
    if(tree[a].key < tree[b].key)//大堆
        swap(a,b);
    tree[a].r = merge(tree[a].r,b);
    set[tree[a].r] = a;//并查
    if(tree[tree[a].l].dis < tree[tree[a].r].dis)
        swap(tree[a].l,tree[a].r);
    if(tree[a].r)
        tree[a].dis = tree[tree[a].r].dis + 1;
    else
        tree[a].dis = 0;
    return a;
}

int pop(int a)
{
    int l = tree[a].l;
    int r = tree[a].r;
    set[l] = l;//因为要暂时删掉根,所以左右子树先作为根
    set[r] = r;
    tree[a].l = tree[a].r = tree[a].dis = 0;
    return merge(l,r);
}

int nextint()
{
    char c;
    int ret = 0;
    while((c = getchar()) > ‘9‘ || c < ‘0‘)
        ;
    ret = c - ‘0‘;
    while((c = getchar()) >= ‘0‘ && c <= ‘9‘)
        ret = ret * 10 + c - ‘0‘;
    return ret;
}

void print(int a)
{
    if(!a)
        return;
    print(a/10);
    putchar(a%10 + ‘0‘);
}

int main()
{
    int a,b,i;
    while(~scanf("%d",&n))
    {
        for(i = 1;i <= n;i ++)
        {;
            tree[i].key = nextint();
            set[i] = i;
            tree[i].l = tree[i].r = tree[i].dis = 0;
        }
        m = nextint();
        while(m --)
        {
            a = nextint();
            b = nextint();
            int ra = find(a);
            int rb = find(b);
            if(ra == rb)
                printf("-1\n");
            else
            {
                int rra = pop(ra);//ra左右子树合并
                tree[ra].key /= 2;
                ra = merge(rra,ra);//重新插入ra 找到合适的位置
                int rrb = pop(rb);
                tree[rb].key /= 2;
                rb = merge(rrb,rb);
                print(tree[merge(ra,rb)].key);
                putchar(10);
            }
        }
    }
    return 0;
}

心若向阳,无谓悲伤

时间: 2024-11-10 11:26:09

hdu1512 Monkey King(并查集,左偏堆)的相关文章

HDU 1512 并查集+左偏树

Monkey King Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 3105    Accepted Submission(s): 1330 Problem Description Once in a forest, there lived N aggressive monkeys. At the beginning, they e

Monkey King HDU - 1512 (左偏树)

Monkey King HDU - 1512 忽然看到左偏树,挺简单的,抄了个模板题练练 1 //左偏树 2 #include <bits/stdc++.h> 3 using namespace std; 4 const int maxn = 100010; 5 struct Node{ 6 int val, dis, l, r; 7 }p[maxn]; 8 int f[maxn]; 9 int gf(int x){ 10 return x == f[x] ? f[x] : f[x] = gf

左偏堆

左偏堆,顾名思义就是向左偏的堆,也就是左边特别重的堆. 左偏堆是一种可并堆,其实现的基本操作是,合并,删除,插入(就是与一个只有一个节点的左偏堆合并) 合并操作 1 int merge(int a,int b){ 2 if (a==0) return b; 3 if (b==0) return a; 4 if (heap[a].v<heap[b].v) swap(a,b); 5 heap[a].r=merge(heap[a].r,b); 6 heap[heap[a].r].f=a; 7 if(h

C++之路进阶——左偏堆(罗马游戏)

F.A.Qs Home Discuss ProblemSet Status Ranklist Contest ModifyUser  gryz2016 Logout 捐赠本站 Notice:由于本OJ建立在Linux平台下,而许多题的数据在Windows下制作,请注意输入.输出语句及数据类型及范围,避免无谓的RE出现. 1455: 罗马游戏 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 1061  Solved: 439[Submit][Status

[HDU1512]Monkey King(左偏树)

用并查集维护猴子们的关系,强壮值用左偏树维护就行了 Code #include <cstdio> #include <algorithm> #include <cstring> #define N 100010 using namespace std; int n,fa[N],m,fr[N]; //fr[x]维护猴子x所在集合在左偏树上的编号 int Find(int x){return x==fa[x]?x:fa[x]=Find(fa[x]);} namespace

[HDU1512]:Monkey King

Monkey King Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 6274    Accepted Submission(s): 2678 Problem Description Once in a forest, there lived N aggressive monkeys. At the beginning, they e

左偏堆(模板)

#include <iostream> #include<cstdio> #define MAX 1010 using namespace std; int tot=0; int v[MAX],l[MAX],r[MAX],d[MAX]; int merge(int x,int y) { if(x==0) return y; if(y==0) return x;//如果有一个子树是空 if(v[x]<v[y]) swap(x,y);//取较大的根节点权值 r[x]=merge(

BZOJ 2333 SCOI2011 棘手的操作 并查集+可并堆

..题意概述就不写了,各位老爷如果是看着玩的可以去搜一下,如果是做题找来的也知道题干的.实际上是题干无法缩减懒得复制ORZ 首先处理一下集合的合并和单点值查询的问题.使用并查集,对于每个点记录标记d表示这个点的实际值比这个点加入并查集的时候的值w大了多少,对于每个点find的时候把这个点到代表元路径上的点的d(不包括代表元)的d加起来更新这个点的d,每一次查询某个点的当前值的时候就先find就可以直接用w+d+代表元的d(特判这个点是不是代表元)回答.特别注意为了保证正确性在merge的时候要把

zoj2334 Monkey King , 并查集,可并堆,左偏树

提交地址:点击打开链接 题意:  N(N<=10^5)只猴子,初始每只猴子为自己猴群的猴王,每只猴子有一个初始的力量值.这些猴子会有M次会面.每次两只猴子x,y会面,若x,y属于同一个猴群输出-1,否则将x,y所在猴群的猴王的力量值减半,然后合并这两个猴群.新猴群中力量值最高的为猴王.输出新猴王的力量值. 分析:涉及集合的查询,合并,取最值. 利用并查集和左偏树即可解决. #include <cstdio> #include <cstring> #include <io