zoj 2334 左偏树+并查集

 

 

 

如题: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));  
 }
 }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值