ABC279F 题解

前言

题目传送门!

或许更好的阅读体验?

并查集应用。

思路

本题精髓在于:我们可以把箱子与球都看成一个点。

这些点中,一部分指的是箱子,一部分指的是球。

对于操作一,我们合并 \(x\)\(y\) 对应的箱子编号。此处是将 \(y\) 归属于 \(x\)

但是,只合并是不行的。例如下图的情景:

我们现在要将 \(1\) 号箱子的球投进 \(2\) 号箱子里。那么只需要合并一下:

但是!如果我们接下来将 \(7\) 号球扔进 \(1\) 号箱子时,就会出问题:

那么怎么避免这种问题呢?我们应该新建一个箱子。在合并时,我们要这样做:

接下来,将 \(7\) 号球扔进 \(1\) 号箱子时,我们把它扔进 \(6\) 号箱子即可。

这样,查询时,只需要输出 \(ans_6 = 1\)


另外两种操作则简单多了:

  • 新建小球:直接模拟合并即可。
  • 查询:若 \(x\) 号球归属于 \(t\) 号箱子,则输出 \(ans_t\) 即可。

那么这题就完美做完啦!

代码

省去了缺省源。

const int N = 9e5 + 5;
namespace UnionSet { //并查集
	int fa[N];
	int get(int x)
	{
		if (fa[x] == x) return x;
		return fa[x] = get(fa[x]);
	}
	void merge(int x, int y)
	{
		int fx = get(x), fy = get(y);
		if (fx != fy) fa[fy] = fx;
	}
}; using namespace UnionSet;
int id[N], ans[N]; //相互映射的关系,id是记录连向哪个箱子,如上面的例子中,id[1]=6
void solve()
{
	int n, q;
	scanf("%d%d", &n, &q);
	int cnt_ball = n, cnt_box = n + q;
	for (int i = 1, mx = n + 2 * q; i <= mx; i++) fa[i] = id[i] = ans[i] = i; //最多会存在 n+2*q 个点
	while (q--)
	{
		int op, x, y;
		scanf("%d%d", &op, &x);
		if (op == 1)
		{
			scanf("%d", &y);
			merge(id[x], id[y]); //合并
			cnt_box++, id[y] = cnt_box, ans[cnt_box] = y;
		}
		else if (op == 2) merge(id[x], ++cnt_ball); //新建
		else printf("%d\n", ans[get(x)]); //查询
	}
}

希望能帮助到大家!

posted @ 2022-11-27 09:58  liangbowen  阅读(64)  评论(0编辑  收藏  举报