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)]); //查询
}
}
希望能帮助到大家!