题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1512
题意:有只猴子,每一只猴子都有一个战斗力,一开始全部不认识,它们会发生
次决斗,当
发生决斗时,它们会拉来战斗力最强的
和
(可能是他们自己),然后
和
的战斗力降至一半,
的所有朋友和
的所有朋友就相互成为朋友,求每一次决斗后
和
的朋友中战斗力最强的猴子的战斗力。
题解:这道题,有频繁的求最大值,更改关键字的值,合并,涉及到堆的合并,所以使用左偏树是个不错的选择。
建立最大堆的左偏树
左偏树插入新节点,和删除最小/大节点的复杂度都是级别的,删除的是最小值,还是最大值取决于建/合并树的时候维护的最大堆还是最小堆。
合并左偏树的时间复杂度是,
是两个左偏树的节点数,简单说一下这个复杂度怎么算来的
前置技能:左偏树距离;一个N节点的左偏树距离最多为;
每一次递归合并的开始,都需要分解其中一颗树,总是把分解出来的右子树参与下一步的合并,
一棵树的距离取决于右子树的距离,而右子树的距离在每次分解中递减,因此每棵树的分解次数不超过它的距离,所以分解的次数不会超过
代码:
#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;
}