zoukankan      html  css  js  c++  java
  • 【HDU1512】Monkey King-左偏树+并查集(左偏树入门题)

    测试地址:Monkey King

    题目大意:有N(N≤100000)只猴子,标号为1~N,每只猴子有一个强壮值,一开始他们互相之间都不认识,但猴子避免不了争吵,它们之间会进行M(M≤100000)次争吵,每次争吵都是标号为a和b的两只互不认识的猴子相互争吵,争吵过后,他们就会分别去找他们所认识的最强壮(强壮值最大)的猴子来决斗,决斗过后决斗的两只猴子的强壮值减半(向下取整),但打斗过后两边的猴子就都会互相认识,互相认识的猴子之间就不会再进行争吵了,你的任务就是对于每次争吵,如果不发生争吵(即两方认识)输出-1,否则输出决斗后两方猴子中最强壮猴子的强壮值。

    做法:初观察这一题,维护互相认识的关系可以用并查集O(N)解决,维护最大的强壮值似乎可以用堆来解决,但是两方猴子互相认识之后,两个堆之间需要合并,如果将一个堆中的元素一个一个插入另一个堆中,复杂度是O(N^2)的,不能接受,这时候就要引出可并堆这个数据结构了,而左偏树则是可并堆的一种实现方法,关于左偏树的教程网上很多这里就不讲了。于是这里我们维护很多个大根堆,每次决斗,删除两个堆的根节点,将它们的强壮值减半,然后再合并回原来的堆中,再将两个堆合并,输出根节点的强壮值即可。并查集可以在合并和删除的过程中一并维护。

    以下是本人代码:

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    int n,m,fa[100010];
    struct leftisttree
    {
      int lc,rc,key,dis;
    }nd[100010];
    
    int find(int x) //并查集中的查找
    {
      int r=x,i=x,j;
      while(fa[r]!=r) r=fa[r];
      while(i!=r) j=fa[i],fa[i]=r,i=j;
      return r;
    }
    
    int merge(int x,int y) //合并堆并返回合并后堆的根
    {
      if (!x) return y;
      if (!y) return x;
      if (nd[x].key<nd[y].key) swap(x,y);
      nd[x].rc=merge(nd[x].rc,y);
      int l=nd[x].lc,r=nd[x].rc;
      fa[r]=x;
      if (nd[l].dis<nd[r].dis) swap(nd[x].lc,nd[x].rc);
      nd[x].dis=nd[nd[x].rc].dis+1;
      return x;
    }
    
    int del(int x) //删除一个堆的根节点并返回左右子树合并后的根
    {
      int l,r;
      l=nd[x].lc,r=nd[x].rc;
      fa[l]=l,fa[r]=r;
      nd[x].lc=nd[x].rc=nd[x].dis=0;
      return merge(l,r);
    }
    
    void solve(int x,int y) //求解决斗后最大的强壮值
    {
      int l,r;
      nd[x].key/=2;nd[y].key/=2;
      l=del(x),r=del(y);
      l=merge(l,x),r=merge(r,y);
      l=merge(l,r);
      printf("%d
    ",nd[l].key);
    }
    
    int main()
    {
      while(scanf("%d",&n)!=EOF)
      {
        nd[0].lc=nd[0].rc=0;nd[0].dis=-1;
        for(int i=1;i<=n;i++)
        {
          scanf("%d",&nd[i].key);
          nd[i].lc=nd[i].rc=nd[i].dis=0;
          fa[i]=i;
        }
        scanf("%d",&m);
        for(int i=1,a,b;i<=m;i++)
    	{
    	  scanf("%d%d",&a,&b);
    	  if (find(a)==find(b)) {printf("-1
    ");continue;}
    	  solve(find(a),find(b));
    	}
      }
      
      return 0;
    }
    


  • 相关阅读:
    实验5 数独游戏界面设计
    实验4 颜色、字符串资源的使用 实验报告
    实验五 操作系统之存储管理
    实验四 主存空间的分配和回收
    实验三 进程调度模拟程序
    实验二作业调度模拟程序(先来先服务(FCFS)调度算法)
    实验八 SQLite数据库操作
    实验七 BindService模拟通信
    实验六 在应用程序中播放音频和视频
    实验五 数独游戏界面设计
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793803.html
Copyright © 2011-2022 走看看