Monkey King(可并堆)

题意

有n只猴子,一开始互不认识,每次有两只猴子要打架,他们会找来自己认识的最厉害的猴子来帮他们打(然而他们就看戏),打完后打架的猴子(不是看戏的)武力值会减少一半(下取整),并且他们也互相认识了,在这时询问他们认识的猴子中最厉害的猴子的武力值,打架的在之前认识输出-1。

n,m<=100000

题解

直接用可并堆就好了,对于打完认识就是合并两个队伍,但是对于武力值减少怎么弄?

就直接把老大踢出去,再重新来过。具体实现就是把老大的ls,rs合并,这样他就出来了,然后再合并他和ls(rs)的堆。

具体实现的时候有一些细节,在代码里面实现。

#include<bits/stdc++.h>
using namespace std;

const int maxn=100005;
int n,m,a[maxn];
int fa[maxn],ls[maxn],rs[maxn],dis[maxn],val[maxn];

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

int merge(int A,int B){
  if(!A) return B;
  if(!B) return A;
  if(val[A]<val[B]) swap(A,B);
  rs[A]=merge(rs[A],B);
  fa[rs[A]]=A;
  if(dis[rs[A]]>dis[ls[A]]) swap(rs[A],ls[A]);
  if(!rs[A]) dis[A]=0;
  else dis[A]=dis[rs[A]]+1;
  return A;
}

int pop(int x){//int 是因为不改变ls,rs的话合并x可能他是叶子节点,就会RE
  fa[ls[x]]=ls[x];
  fa[rs[x]]=rs[x];
  fa[x]=x;
  int ret=merge(ls[x],rs[x]);
  ls[x]=rs[x]=0;//求出上面才能改
  return ret;
}

int main(){
  //freopen("testdata.in","r",stdin);
  while(scanf("%d",&n)!=EOF){
    for(int i=1;i<=n;i++){
      scanf("%d",&val[i]);
      fa[i]=i;
      ls[i]=rs[i]=dis[i]=0;
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
      int x,y;
      scanf("%d%d",&x,&y);
      int dx=find(x),dy=find(y);
      if(dx==dy){printf("-1
");continue;}
      //printf("*--------%d %d------------*
",dx,dy);
      int xx=pop(dx),yy=pop(dy);
      //printf("*---------%d %d------*
",xx,yy);
      val[dx]>>=1;
      val[dy]>>=1;
      merge(dx,find(xx));
      merge(dy,find(yy));
      merge(find(dx),find(dy));
      //for(int j=1;j<=n;j++) printf("%d ",fa[j]);
      //putchar(10);
      printf("%d
",val[find(dx)]);
    }
  }
  return 0;
}
View Code
原文地址:https://www.cnblogs.com/sto324/p/11291530.html