【ybt金牌导航4-1-2】【luogu P2713】合并游戏 / 罗马游戏

52 篇文章 1 订阅

合并游戏 / 罗马游戏

题目链接:ybt金牌导航4-1-2 / luogu P2713

题目大意

有一个序列,一开始各自在各自的集合中。
你要支持两个操作,把两个数所在的集合合并,求出一个数所在集合中最小的数并把它删除。
如果询问的数已经被删除,那就直接跳过。(求最小数就还要输出 0 0 0

思路

看到求集合中最小,自然想到线段树,这个那个树之类的。
但你你又看到合并集合,那就是左偏树。

那你就记录一下左偏树上的这个点代表数列的哪个数,然后从键值是从小到大,然后就搞合并和删除两个操作。
然后置于你删除的数在哪个集合就搞个并查集维护一下就好了。

记得判断这个点是否被删除,如果删除就直接是跳过。
当然如何合并操作中的两个点已经在同一个集合,就不用合并了。

代码

#include<cstdio>
#include<algorithm>

using namespace std;

struct left_tree {
	int x, l, r, dis, dy;
}a[1000001];
int n, m, fa[1000001], x, y, root[1000001];
int dead[1000001];
char c;

int find(int now) {
	if (fa[now] == now) return now;
	return fa[now] = find(fa[now]);
}

int merge(int x, int y) {
	if (!x) return y;
	if (!y) return x;
	
	if (a[x].x > a[y].x) swap(x, y);
	a[x].r = merge(a[x].r, y);
	if (a[a[x].r].dis > a[a[x].l].dis) swap(a[x].l, a[x].r);
	a[x].dis = a[a[x].r].dis + 1;
	
	return x;
}

int delete_top(int x) {
	return merge(a[x].l, a[x].r);
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &a[i].x);
		a[i].dy = i;
		root[i] = i;
		fa[i] = i;
	}
	
	scanf("%d", &m);
	while (m--) {
		c = getchar();
		while (c != 'M' && c != 'K') c = getchar();
		
		if (c == 'M') {
			scanf("%d %d", &x, &y);
			if (dead[x] || dead[y]) continue;//已经死了其中一个
			int X = find(x), Y = find(y);
			if (X == Y) continue;//已经合并了
			fa[X] = Y;
			root[Y] = merge(root[X], root[Y]);
		}
		else {
			scanf("%d", &x);
			int X = find(x);
			if (dead[x]) printf("0\n");//这个人已近死了
				else {
					printf("%d\n", a[root[X]].x);
					dead[a[root[X]].dy] = 1;//标记这个人被杀死
					root[X] = delete_top(root[X]);
				}
		}
	}
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值