zoj 2334 Monkey King/左偏树+并查集

本文介绍了一种使用并查集和左偏树解决猴子冲突问题的方法。该问题涉及通过决斗来解决不同群体间的冲突,并更新群体内最强战斗力的过程。文章提供了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原题链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1389

大致题意:N只相互不认识的猴子(每只猴子有一个战斗力值)

两只不认识的猴子之间发生冲突,两只猴子会分别请出它们认识的最强壮的猴子进行决斗。

决斗之后这,两群猴子都相互认识了。决斗的那两只猴子战斗力减半。。。

有m组询问,输入a b表示猴子a和b发生了冲突,若a,b属于同一个集合输出-1

否则输出决斗之后这群猴子(已合并)中最强的战斗力值。。。

具体思路:用并查集判断是否属于同一集合,用左偏树维护一群猴子的战斗力值。

加了垃圾回收,否则容易爆内存。。。

具体如下:

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
const int Max_N = 100050;
struct UnionFind{
	int par[Max_N], rank[Max_N];
	inline void init(int n){
		for (int i = 1; i <= n; i++){
			par[i] = i;
			rank[i] = 0;
		}
	}
	inline int find(int x){
		while (x != par[x]){
			x = par[x] = par[par[x]];
		}
		return x;
	}
	inline void unite(int x, int y){
		x = find(x), y = find(y);
		if (x == y) return;
		if (rank[x] < rank[y]){
			par[x] = y;
		} else {
			par[y] = x;
			if (rank[x] == rank[y]) rank[x]++;
		}
	}
};
struct Node{
	int v, npl;
	Node *ch[2];
	inline void set(int _v = 0, int _npl = -1, Node *p = NULL){
		v = _v, npl = _npl;
		ch[0] = ch[1] = p;
	}
	inline void push_up(){
		npl = ch[1]->npl + 1;
	}
};
struct LeftistTree{
	int N, top;
	UnionFind rec;
	Node *tail, *null;
	Node stack[Max_N], *ptr[Max_N], *store[Max_N];
	void init(int n){
		tail = &stack[0];
		null = tail++;
		null->set();
		N = n, top = 0, rec.init(n);
	}
	inline Node *newNode(int v){
		Node *p = null;
		if (!top) p = tail++;
		else p = store[--top];
		p->set(v, 0, null);
		return p;
	}
	inline Node* Merge(Node* &x, Node* &y){
		if (x == null) return y;
		if (y == null) return x;
		if (y->v > x->v) std::swap(x, y);
		x->ch[1] = Merge(x->ch[1], y);
		if (x->ch[1]->npl > x->ch[0]->npl) std::swap(x->ch[0], x->ch[1]);
		x->push_up();
		return x;
	}
	inline int get_max(int i){
		return ptr[i]->v;
	}
	inline void insert(){
		int v;
		for (int i = 1; i <= N; i++){
			scanf("%d", &v);
			ptr[i] = newNode(v);
		}
	}
	inline void del(int i){
		int ret = get_max(i);
		Node *x = newNode(ret >> 1);
		store[top++] = ptr[i];
		ptr[i] = Merge(ptr[i]->ch[0], ptr[i]->ch[1]);
		ptr[i] = Merge(ptr[i], x);
	}
	inline void gogo(int a, int b){
		int ans = 0;
		a = rec.find(a), b = rec.find(b);
		if (a == b){
			printf("-1\n");
			return;
		}
		rec.unite(a, b);
		del(a), del(b);
		if (rec.rank[a] > rec.rank[b]){
			ptr[a] = Merge(ptr[a], ptr[b]);
			ans = ptr[a]->v;
		} else {
			ptr[b] = Merge(ptr[a], ptr[b]);
			ans = ptr[b]->v;
		}
		printf("%d\n", ans);
	}
}lft;
int main(){
#ifdef LOCAL
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w+", stdout);
#endif
	int n, m, a, b;
	while (~scanf("%d", &n)){
		lft.init(n), lft.insert();
		scanf("%d", &m);
		while (m--){
			scanf("%d %d", &a, &b);
			lft.gogo(a, b);
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值