【题解】AT_abc293_d 题解
AT_abc293_d 题解
思路分析
一道并查集裸题。
为什么要使用并查集呢?因为题目要求连接两条绳子,同时还要判环,所以我们把绳子抽象成点,连接两条绳子抽象为用一条边连接两个点。于是就可以参考克鲁斯卡尔算法的思路,使用并查集来判环:如果两条绳子已经被连接在一条链(由绳子连接而成的连通分量)上的话,又被连接一次,就产生了环,此时产生了环的链数加一。
那么题目又要求统计非环链数,又该怎么办呢?很简单,同样使用并查集统计所有链数,一个祖先代表一条链。然后用总链数减去产生了产生了环的链数就可以了。接着按照上述步骤实现即可,注意并查集要初始化。
代码
#include <iostream>
using namespace std;
const int N = 2e5 + 10;
int fa[N]; //并查集父亲数组
bool vis[N]; //判断祖先是否重复的数组
int find(int x) //寻找祖先
{
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void init(int p) //初始化并查集
{
for(int i = 1;i <= p;i++)
{
fa[i] = i;
}
}
void merge(int i, int j) //合并两条绳子
{
fa[find(i)] = find(j);
}
int main()
{
int n, m;
cin >> n >> m;
int x = 0, z = 0; //x 用于记录有环个数,z 用来记录总链数
init(n); //初始化并查集
for(int i = 1;i <= m;i++)
{
int q, p;
char c;
cin >> q >> c >> p >> c;
if(find(q) != find(p)) //如果不在同一个链上
{
merge(q, p); //合并
}
else
{
x++; //已经在同一个链上,又要连接一次,就会形成环,此时有环个数加一。
}
}
for(int i = 1;i <= n;i++) //遍历所有点,分别查找其祖先
{
int r = find(i); //查找祖先
if(!vis[r]) //如果祖先没有计算过
{
z++; //计算该祖先,一个祖先代表一条链。
vis[r] = true; //标记改祖先已被访问。
}
}
cout << x << " " << z - x << endl; //输出答案,非环链数为总链数减有环链数的差
return 0;
}

浙公网安备 33010602011771号