题意
有n只猴子,一开始互不认识,每次有两只猴子要打架,他们会找来自己认识的最厉害的猴子来帮他们打(然而他们就看戏),打完后打架的猴子(不是看戏的)武力值会减少一半(下取整),并且他们也互相认识了,在这时询问他们认识的猴子中最厉害的猴子的武力值,打架的在之前认识输出-1。
n,m<=100000
题解
直接用可并堆就好了,对于打完认识就是合并两个队伍,但是对于武力值减少怎么弄?
就直接把老大踢出去,再重新来过。具体实现就是把老大的ls,rs合并,这样他就出来了,然后再合并他和ls(rs)的堆。
具体实现的时候有一些细节,在代码里面实现。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; }