题解 洛谷 P1196 [NOI2022] 银河英雄传说
Posted WuWh
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解 洛谷 P1196 [NOI2022] 银河英雄传说相关的知识,希望对你有一定的参考价值。
根据题目意思我们可以猜看出来这是一道需要用到并查集的题。
但由于题目不仅要求判断战舰是否在同一列中,还要求回答中间相隔多少战舰。这样需要在并查集上维护一些额外信息的并查集我们称之为带权并查集。
根据前缀和的思想,我们设 \\(size[x]\\) 表示 \\(x\\) 到 \\(x\\) 所在的树的根的距离(包括根但不包括 \\(x\\) )。那么对于 C i j
的答案就可以用 \\(\\text{abs}(size[i]-size[j])-1\\) 来回答。
那么最麻烦的事情就是如何维护了。首先考虑相对简单的 \\(\\textbf{find(int x)}\\) 函数。由于采用路径压缩并查集,因此需要在路径压缩时改为到根的路径上的边权值和。
int find(int x) {
if (x == fa[x]) return x;
int root = find(fa[x]); // 顺序不能错,必须先递归,以更新后面的信息
size[x] += size[fa[x]];
return fa[x] = root;
}
在 \\(\\textbf{merge(int x,int y)}\\) 时,我们的策略是把 \\(x\\) 的树根作为 \\(y\\) 的树根的孩子。因此,\\(\\textbf{root}(x)\\to \\textbf{root}(y)\\) 的这条边的权值应为原先整个 \\(\\textbf{root}(y)\\) 树的权值。因此我们还需要维护一个 \\(g[]\\) ,其中 \\(g[x]\\) 表示 \\(x\\) 所在的子树的边权之和。
记 \\(x\'=\\textbf{root}(x),y\'=\\textbf{root}(y)\\) ,则我们需要做的更新:
- \\(fa[x\']=y\'\\);
- \\(size[x\']=g[y\']\\) ;
- \\(g[y\']\\gets g[y\']+g[x\']\\) 。
这样一来,我们便成功维护了所需的信息!
需要注意的:
- 一开始 \\(g[i]=1,size[i]=0\\) (根据二者定义即可理解);
- 为什么查询结果一定正确/最新?答:每次询问前都会路径压缩。
完整 AC 代码
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
int fa[300050], size[300050], g[300050];
void init() { for (int i = 1; i <= 30000; i++) { fa[i] = i; g[i] = 1;} }
int find(int x) {
if (x == fa[x]) return x;
int root = find(fa[x]);
size[x] += size[fa[x]];
return fa[x] = root;
}
void merge(int x, int y) {
int fx = find(x), fy = find(y);
// if (fx == fy) return ;
fa[fx] = fy;
size[fx] = g[fy];
g[fy] += g[fx];
return ;
}
int main() {
int T; init();
scanf("%d", &T);
while (T--) {
char opt; int i, j;
cin >> opt;
scanf("%d%d", &i, &j);
if (opt == \'M\') merge(i, j);
else { if (find(i) == find(j) ) printf("%d\\n", abs(size[i] - size[j]) - 1); else puts("-1"); }
}
return 0;
}
以上是关于题解 洛谷 P1196 [NOI2022] 银河英雄传说的主要内容,如果未能解决你的问题,请参考以下文章