左偏树。
每次合并的时候取出堆顶,然后合并堆顶的左右儿子,最后将堆顶所代表的猴子的强壮值减半扔到左偏树里。
维护父亲的时候用并查集实现。
实现的时候有些细节需要注意。
CODE:
#include<cstdio>
#include<iostream>
using namespace std;
struct node
{
int num,dis,id;
node *ch[2];
}pool[300005],*t[300005],*null;
int f[300005];
int n,m,x,y,tot;
inline node *getnew(int value,int id)
{
node *now=pool+ ++tot;
now->ch[0]=now->ch[1]=null;
now->num=value,now->id=id;
now->dis=0;
return t[tot]=now;
}
inline int find(int n)
{
if(f[n]!=n) f[n]=find(f[n]);
return f[n];
}
node *merge(node *x,node *y)
{
if(x==null) return y;
if(y==null) return x;
if(x->num<y->num||(x->num==y->num&&x->id>y->id)) swap(x,y);
x->ch[1]=merge(x->ch[1],y);
if(x->ch[0]->dis<x->ch[1]->dis) swap(x->ch[0],x->ch[1]);
x->dis=x->ch[1]->dis+1;
return x;
}
inline int Merge(int x,int y)
{
node *t1=t[x],*t2=t[y];
int num1=t1->num,num2=t2->num;
t1=merge(t1->ch[0],t1->ch[1]);
t2=merge(t2->ch[0],t2->ch[1]);
if(t1!=null)
{
f[t1->id]=t1->id;
if(t1->ch[0]!=null) f[t1->ch[0]->id]=t1->id;
if(t1->ch[1]!=null) f[t1->ch[1]->id]=t1->id;
}
if(t2!=null)
{
f[t2->id]=t2->id;
if(t2->ch[0]!=null) f[t2->ch[0]->id]=t2->id;
if(t2->ch[1]!=null) f[t2->ch[1]->id]=t2->id;
}
t1=merge(t1,getnew(num1>>1,tot+1)),f[tot]=t1->id;
t2=merge(t2,getnew(num2>>1,tot+1)),f[tot]=t2->id;
node *root=merge(t1,t2);
f[x]=f[y]=f[t1->id]=f[t2->id]=root->id;
return t[find(t1->id)]->num;
}
int main()
{
freopen("monkeyk.in","r",stdin);
freopen("monkeyk.out","w",stdout);
null=pool;
null->ch[0]=null->ch[1]=null;
null->dis=-1;
while(scanf("%d",&n)!=EOF)
{
tot=0;
for(int i=1;i<=n;i++)
scanf("%d",&x),getnew(x,i);
scanf("%d",&m);
for(int i=1;i<=n+m;i++)
f[i]=i;
while(m--)
{
scanf("%d%d",&x,&y);
x=find(x),y=find(y);
if(x!=y) printf("%d\n",Merge(x,y));
else printf("-1\n");
}
}
return 0;
}