如题:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2334
N只猴子打架,打架每次找自己朋友里战斗力最强的,并且不能认识,2只最强猴子打完后,不认识变认识,战斗力减半,一开始每个猴子是独立的集合,都不认识,他的朋友只有他自己。输出打完架后最大战力
需要快速取出最大战力,删除,并插入,并且不停的合并2个集合。满足左偏树的要求。加上并查集,即可解。
#include<iostream>
using namespace std;
#define MAXN 100005
//左偏树为了尽快访问最小或最大节点,并在修改后快速恢复堆性质
int f[MAXN]; //集合
int root[MAXN]; //记录根节点下标
typedef struct node
{
int dist,left,right,fight; //左偏树的根节点距离,左,右子树,战斗力
}Tree;
Tree tree[MAXN]; //左偏树
int find(int x) //集合查找
{
if(f[x]==-1) return x;
f[x]=find(f[x]);
return f[x];
}
void Union(int x,int y) //集合合并
{
f[find(x)]=find(y);
}
int merge(int a,int b) //2颗左偏树的合并
{
if(a==0) return b;
if(b==0) return a;
if(tree[a].fight<tree[b].fight) //调整根战力最大
swap(a,b);
tree[a].right=merge(tree[a].right,b); //合并a的右子树和b
if(tree[tree[a].left].dist<tree[tree[a].right].dist) //调整左右子树,dist(左子树)>sidt(右子树)
swap(tree[a].left,tree[a].right);
if(tree[a].right==0)
tree[a].dist=0;
else
tree[a].dist=tree[tree[a].right].dist+1;
return a;
}
int solve(int x,int y) //x,y猴子打架,先找到他们的集合,
//然后根删掉,合并左右子树,x,y都要这样,然后x,y和删掉的根合并,最后合并x,y
{
if(find(x)==find(y)) return -1;
int root_x=root[find(x)],root_y=root[find(y)]; //找到x,y集合战力最强的2只猴子
tree[root_x].fight/=2;
tree[root_y].fight/=2;
int tx=merge(tree[root_x].left,tree[root_x].right); //删除根
tree[root_x].left=tree[root_x].right=0;
tx=merge(tx,root_x); //再合并回去
int ty=merge(tree[root_y].left,tree[root_y].right);
tree[root_y].left=tree[root_y].right=0;
ty=merge(ty,root_y);
Union(x,y); //2颗调整过的树最终合并
root[find(x)]=merge(tx,ty); //根下标的更新
return tree[root[find(x)]].fight;
}
int main()
{
int N;
while(~scanf("%d",&N))
{
int i;
for(i=1;i<=N;i++)
{
int fi;
tree[i].left=tree[i].right=tree[i].dist=0;
scanf("%d",&fi);
tree[i].fight=fi;
f[i]=-1; //集合初始化-1
root[i]=i; //根是自己
}
int M;
scanf("%d",&M);
for(i=1;i<=M;i++)
{
int x,y;
scanf("%d %d",&x,&y);
printf("%d\n",solve(x,y));
}
}
}