AcWing 837. 连通块中点的数量(查并集)

本文介绍了一种解决无向图连通性问题的方法,通过查并集模板实现边的添加、判断两点是否连通以及查询连通块大小的功能。题目给出的数据范围是1≤n,m≤10^5,对于每个询问,我们维护一个祖宗节点和联通点的大小数组,通过路径压缩和集合合并来高效地处理操作。代码中定义了find_、merge_和ask三个关键函数,分别用于查找祖宗节点、合并连通集和判断两点是否在同一连通块中。
摘要由CSDN通过智能技术生成

题目描述:

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

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

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

输入格式:

第一行输入整数 n 和 m。

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

输出格式:

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

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

每个结果占一行。

数据范围:

1≤n,m≤10^5

输入样例:

5 5
C 1 2
Q1 1 2
Q2 1
C 2 5
Q2 5

输出样例:

Yes
2
3

思路:本题为查并集的模板,主要围绕查并集的基本操作。

集合初始化:将每个节点的祖宗节点等于他本身,初始联通点的大小为1。

for(int i=1;i<=n;i++)
 {
     v[i]=i;num[i]=1;
 }

寻找祖宗节点:由于只有只有祖宗节点的p[x]值等于他自己,当该节点不是祖宗节点时要向上寻找他的祖宗节点。同时进行路径压缩,将所有与根节点相连的节点变为叶子节点。

int find_(int x)
{
    if(v[x]!=x) v[x]=find_(v[x]);
    /*
    可以发现,每个集合中只有祖宗节点的p[x]值等于他自己,即:
    p[x]=x;
    */
    return v[x];//找到了便返回祖宗节点的值
}

集合合并:将右节点的祖宗节点赋为左节点的祖宗节点。同时更新右节点联通集的大小,即右=右+左。

void merge_(int l,int r)
{
    int x=find_(l),y=find_(r);
    v[x]=y;
    num[y]+=num[x];
}

判断是否在同一联通集:看他们的祖宗节点是否相等。

int ask(int l,int r)
{
    return find_(l)==find_(r);
}

代码如下:

#include <bits/stdc++.h>
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define endl '\n'
#define N  100005
using namespace std;
const int inf=0x3f3f3f3f;
const double ex=1e-7;
const int mod=1e9+7;
int gcd(int a ,int b){ return b ? gcd(b,a%b) : a ;}
string st;
int v[N],num[N];
int find_(int x)
{
    if(v[x]!=x) v[x]=find_(v[x]);
    return v[x];
}
int ask(int l,int r)
{
    return find_(l)==find_(r);
}
void merge_(int l,int r)
{
    int x=find_(l),y=find_(r);
    v[x]=y;
    num[y]+=num[x];
}
signed main()
{ios
    int n,m,l,r,k;
    string op;
    cin >>n>>m;
    for(int i=1;i<=n;i++)
    {
        v[i]=i;num[i]=1;
    }
    while(m--)
    {
        cin >>op;
        if(op=="C")
        {
           cin >>l>>r;
           if(!ask(l,r))
           {
               merge_(l,r);
           }
        }else if(op=="Q1")
        {
           cin >>l>>r;
           if(ask(l,r))
           {
               cout <<"Yes"<<endl;
           }else{
               cout <<"No"<<endl;
           }
        }else{
           cin >>k;
           int ans=find_(k);
           cout <<num[ans]<<endl;
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值