Monkey King(左偏树)

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

题意:有N只猴子,每一只猴子都有一个战斗力,一开始全部不认识,它们会发生M次决斗,当X,Y发生决斗时,它们会拉来战斗力最强的{X}'{Y}'(可能是他们自己),然后{X}'{Y}'的战斗力降至一半,X的所有朋友和Y的所有朋友就相互成为朋友,求每一次决斗后XY的朋友中战斗力最强的猴子的战斗力。

题解:这道题,有频繁的求最大值,更改关键字的值,合并,涉及到堆的合并,所以使用左偏树是个不错的选择。

建立最大堆的左偏树

左偏树插入新节点,和删除最小/大节点的复杂度都是O\left ( logn \right )级别的,删除的是最小值,还是最大值取决于建/合并树的时候维护的最大堆还是最小堆。

合并左偏树的时间复杂度是O\left ( log(N1+1) -1 + log(N2+1) -1\right )=O\left ( logN1 + logN2 \right ),N1,N2是两个左偏树的节点数,简单说一下这个复杂度怎么算来的

前置技能:左偏树距离;一个N节点的左偏树距离最多为log(N+1)-1

每一次递归合并的开始,都需要分解其中一颗树,总是把分解出来的右子树参与下一步的合并,

一棵树的距离取决于右子树的距离,而右子树的距离在每次分解中递减,因此每棵树的分解次数不超过它的距离,所以分解的次数不会超过 O\left ( logN1 + logN2 \right )

 

代码

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define Pair pair<int,int>
//#define int long long
#define fir first
#define sec second
namespace fastIO {
#define BUF_SIZE 100000
#define OUT_SIZE 100000
	//fread->read
	bool IOerror = 0;
	//inline char nc(){char ch=getchar();if(ch==-1)IOerror=1;return ch;}
	inline char nc() {
		static char buf[BUF_SIZE], * p1 = buf + BUF_SIZE, * pend = buf + BUF_SIZE;
		if (p1 == pend) {
			p1 = buf; pend = buf + fread(buf, 1, BUF_SIZE, stdin);
			if (pend == p1) { IOerror = 1; return -1; }
		}
		return *p1++;
	}
	inline bool blank(char ch) { return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'; }
	template<class T> inline bool read(T& x) {
		bool sign = 0; char ch = nc(); x = 0;
		for (; blank(ch); ch = nc());
		if (IOerror)return false;
		if (ch == '-')sign = 1, ch = nc();
		for (; ch >= '0' && ch <= '9'; ch = nc())x = x * 10 + ch - '0';
		if (sign)x = -x;
		return true;
	}
	inline bool read(double& x) {
		bool sign = 0; char ch = nc(); x = 0;
		for (; blank(ch); ch = nc());
		if (IOerror)return false;
		if (ch == '-')sign = 1, ch = nc();
		for (; ch >= '0' && ch <= '9'; ch = nc())x = x * 10 + ch - '0';
		if (ch == '.') { double tmp = 1; ch = nc(); for (; ch >= '0' && ch <= '9'; ch = nc())tmp /= 10.0, x += tmp * (ch - '0'); }
		if (sign)x = -x; return true;
	}
	inline bool read(char* s) {
		char ch = nc();
		for (; blank(ch); ch = nc());
		if (IOerror)return false;
		for (; !blank(ch) && !IOerror; ch = nc())* s++ = ch;
		*s = 0;
		return true;
	}
	inline bool read(char& c) {
		for (c = nc(); blank(c); c = nc());
		if (IOerror) { c = -1; return false; }
		return true;
	}
	template<class T, class... U>bool read(T& h, U& ... t) { return read(h) && read(t...); }
#undef OUT_SIZE
#undef BUF_SIZE
}; using namespace fastIO; using namespace std;

const int N = 1e5 + 5;
const double eps = 1e-7;
const double pi = acos(-1.0);
const int mod = 998244353;


int f[N];
struct Tree{
  int value,dist;
  Tree *left,*right;
};
Tree *tree[N];
int distance(Tree *t){return t==NULL?0:t->dist;}
void fixdist(Tree *t){if(distance(t->left)<distance(t->right))swap(t->left,t->right);t->dist=distance(t->right)+1;}
Tree *merger(Tree *a,Tree *b){
  if(a==NULL)return b;
  if(b==NULL)return a;
  if(b->value>a->value)swap(a,b);
  a->right=merger(a->right,b);
  fixdist(a);
  return a;
}
Tree *delMax(Tree *t){
  if(t!=NULL)return merger(t->left,t->right);
  return NULL;
}
void init(Tree *&t,int value){
  t=new Tree;
  t->dist=1;
  t->value=value;
  t->left=t->right=NULL;
}

int find(int x){
   int y,root,t;
   y=x;
   while(f[y]>0)y=f[y];
   root=y;
   y=x;
   while(f[y]>0){
     t=f[y];
     f[y]=root;
     y=t;
   }
   return root;
}
int _union(int fx,int fy){
  if(f[fx]<f[fy]){
    /******/
    f[fx]+=f[fy];
    return f[fy]=fx;
    /******/
  }else{
    /******/
    f[fy]+=f[fx];
    return f[fx]=fy;
    /******/    
  }
}

void slove(int u,int v){
  int fu=find(u);
  int fv=find(v);
  Tree *p1,*p2,*p3,*p4;
  if(fu==fv){
    printf("-1\n");return;
  }
  init(p1,tree[fu]->value/2);
  p2=delMax(tree[fu]);
  p2=merger(p1,p2);  
  init(p3,tree[fv]->value/2);
  p4=delMax(tree[fv]);
  p4=merger(p3,p4);
  fv=_union(fu,fv);
  tree[fv]=merger(p2,p4);
  printf("%lld\n",tree[fv]->value);
}
signed main() {
  int n;
  while(read(n)){
    for(int i=1;i<=n;i++){
      int d;read(d);
      init(tree[i],d);
    }
    memset(f,-1,sizeof f);
    int q;read(q);
    while(q--){
      int u,v;read(u,v);
      slove(u,v);
    }
    for(int i=1;i<=n;i++)delete tree[i];
  }
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值