并查集的第二节

给定一个包含 nn 个点(编号为 1∼n1∼n)的无向图,初始时图中没有边。

现在要进行 mm 个操作,操作共有三种:

  1. C a b,在点 aa 和点 bb 之间连一条边,aa 和 bb 可能相等;
  2. Q1 a b,询问点 aa 和点 bb 是否在同一个连通块中,aa 和 bb 可能相等;
  3. Q2 a,询问点 aa 所在连通块中点的数量;
输入格式

第一行输入整数 nn 和 mm。

接下来 mm 行,每行包含一个操作指令,指令为 C a bQ1 a b 或 Q2 a 中的一种。

输出格式

对于每个询问指令 Q1 a b,如果 aa 和 bb 在同一个连通块中,则输出 Yes,否则输出 No

对于每个询问指令 Q2 a,输出一个整数表示点 aa 所在连通块中点的数量

每个结果占一行。

数据范围

1≤n,m≤1051≤n,m≤105

输入样例:
5 5
C 1 2
Q1 1 2
Q2 1
C 2 5
Q2 5
输出样例:
Yes
2
3
难度:简单
时/空限制:1s / 64MB
总通过数:80911
总尝试数:161041
来源:

模板题

#include

using namespace std;

const int N = 1e5 + 10;

int s[N], component_size[N]; // 修改了这里,将 size 改为 component_size

int find(int x) {

if (x != s[x]) {

s[x] = find(s[x]); // 路径压缩

}

return s[x];

}

void merge(int a, int b) {

int pa = find(a);

int pb = find(b);

if (pa != pb) {

if (component_size[pa] < component_size[pb]) { // 按秩合并:把小树合并到大树

s[pa] = pb;

component_size[pb] += component_size[pa];

} else {

s[pb] = pa;

component_size[pa] += component_size[pb];

}

}

}

int main() {

int n, m;

cin >> n >> m;

// 初始化并查集

for (int i = 1; i <= n; i++) {

s[i] = i; // 父节点指向自己

component_size[i] = 1; // 每个点初始时大小为1

}

while (m--) {

string op;

int a, b;

cin >> op;

if (op == "Q1") {

cin >> a >> b;

if (find(a) == find(b)) {

cout << "Yes" << endl;

} else {

cout << "No" << endl;

}

} else if (op == "Q2") {

cin >> a;

cout << component_size[find(a)] << endl; // 查询 a 所在连通块的大小

} else if (op == "C") {

cin >> a >> b;

merge(a, b);

}

}

return 0;

}

你可能感兴趣的:(算法,图论,数据结构)