一. 题目

image.png

二. 理论

image.png

image.png

三. 代码

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
const int N=1e5+10;

int n, m;

class JoinSet
{
public://cnt[i]是i节点所在的集合中点的数量
    int p[N], cnt[N];//p[i]是点i节点的父亲,根的父亲是自己
public:
    JoinSet()
    {
        initIO();
    }

    void initIO()
    {
        // 关闭输入输出缓存,使效率提升
        ios::sync_with_stdio(false);
        // 解除cin和cout的默认绑定,来降低IO的负担使效率提升
        cin.tie(nullptr);
        cout.tie(nullptr);
    }

    void init()
    {
        for(int i=1;i<=n;++i)
            p[i]=i, cnt[i]=1;
    }

    //合并b的集合到a集合中
    void add(int a, int b)
    {
        a=find(a), b=find(b);//找根
        if(a==b) return;//已经是同一集合
        p[b]=a, cnt[a]+=cnt[b];//更新
    }

    //查找点x所在集合的树根代表节点, 一旦查找过一遍,则路径上所有点都指向根
    int find(int x)
    {//若当前点不是祖宗节点, 且当前点的父节点不是祖宗节点, 向上寻找
        if(p[x]!=x&&p[x]!=p[p[x]])
            p[x]=find(p[x]);
        return p[x];
    }
}js;

int main()
{
    cin>>n>>m;
    js.init();

    while(m--)
    {
        string op;
        int a, b;

        cin>>op;
        if(op=="C")
        {
            cin>>a>>b;
            js.add(a, b);
        }
        else if(op=="Q1")
        {
            cin>>a>>b;
            if(js.find(a)==js.find(b)) cout<<"Yes";
            else cout<<"No";
            cout<<endl;
        }
        else if(op=="Q2")
        {
            cin>>a;
            cout<<js.cnt[js.find(a)]<<endl;
        }
    }
}