题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2334
题意:N个猴子,打了M次架,每次打架的时候双方猴子都会派出自己一方战斗力最高的猴子,打完架后这两只猴子的战斗力都减半。同时,不打不相识嘛,两方猴子打完架后就互相认识变成同伙不会再打架。输出每次打架后通过这次冲突合并的猴子中战斗力最高的猴子的战斗力值,如果冲突双方在同一伙的话直接输出-1。
思路:对于判断两个猴子是不是在一个里面直接用并查集就好。然后,两个群体的合并直接用左偏树的merge就好。这里显然是根节点存储最大值。那么当询问两个猴子时,我们分别找到其所在群里中最牛逼的猴子,也就是在其所在子树的根节点的那个猴子,将其减半后删掉再插入,最后将两个子树合并,输出根节点的值就好。在这里,怎么找到其所在子树的根节点呢?一开始我加了个parent指针每次向上找,其实每次合并时用并查集中的根节点的指针指向合并后树的根节点就好,就不用每次沿着parent向上找了,只用并查集找到的就行。
struct node
{
int val,Max;
int dis;
node *c[2];
};
node a[N],*root[N],*nullNode;
int cnt;
node *newNode(int val)
{
node *e=&a[cnt++];
e->c[0]=e->c[1]=nullNode;
e->Max=e->val=val;
e->dis=0;
return e;
}
void init()
{
cnt=0;
nullNode=0;
nullNode=newNode(0);
nullNode->dis=-1;
nullNode->Max=-INF;
}
void pushUp(node *x)
{
if(x==nullNode) return;
x->Max=max(x->val,max(x->c[0]->Max,x->c[1]->Max));
x->dis=x->c[1]->dis+1;
}
node *merge(node *a,node *b)
{
if(a==nullNode) return b;
if(b==nullNode) return a;
if(a->val<b->val) swap(a,b);
a->c[1]=merge(a->c[1],b);
if(a->c[0]->dis<a->c[1]->dis) swap(a->c[0],a->c[1]);
pushUp(a);
return a;
}
node *del(node *&a)
{
return merge(a->c[0],a->c[1]);
}
int S[N],n,m,s[N];
int find(int x)
{
if(s[x]!=x) s[x]=find(s[x]);
return s[x];
}
void solve(int x,int y)
{
int xx=find(x),yy=find(y);
if(xx==yy)
{
puts("-1");
return;
}
s[xx]=yy;
node *p1=newNode(root[xx]->Max>>1);
node *p2=newNode(root[yy]->Max>>1);
node *p3=del(root[xx]);
node *p4=del(root[yy]);
p3=merge(p1,p3);
p4=merge(p2,p4);
root[yy]=merge(p3,p4);
PR(root[yy]->Max);
}
int main()
{
Rush(n)
{
int i;
FOR1(i,n) RD(S[i]);
init();
FOR1(i,n) root[i]=newNode(S[i]);
FOR1(i,n) s[i]=i;
RD(m);
int x,y;
while(m--)
{
RD(x,y);
solve(x,y);
}
}
}