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

地址 https://www.acwing.com/problem/content/description/839/

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

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

“C a b”,在点a和点b之间连一条边,a和b可能相等;
“Q1 a b”,询问点a和点b是否在同一个连通块中,a和b可能相等;
“Q2 a”,询问点a所在连通块中点的数量;
输入格式
第一行输入整数n和m。

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

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

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

每个结果占一行。

数据范围
1≤n,m≤105
输入样例:
5 5
C 1 2
Q1 1 2
Q2 1
C 2 5
Q2 5
输出样例:
Yes
2
3

算法1
(暴力枚举) O(n2)O(n2)
起始每次遇到并查集的题目,总会下意识的先去想想哈希。
但是哈希的合并那就逊色多了。不过这次也AC了。
计算相同集合里的个数要注意了,尽量在合并的时候注意次序,
序号小的统计记录不重复的记录下之后的所有大序号元素的个数。

序号大的元素记录里不要统计小序号元素个数,避免重复。

C++ 代码

#include <iostream>
#include <map>
#include <set>
using namespace std;


/*
//并查集模板
void init(int n)
{
    for(int i=1;i<=n;i++)
        fa[i]=i;
}
int get(int x)
{
    return fa[x]==x?x:fa[x]=get(fa[x]);//路径压缩,防止链式结构
}
void merge(int x,int y)
{
    fa[get(x)]=get(y);
}

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

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

“C a b”,在点a和点b之间连一条边,a和b可能相等;
“Q1 a b”,询问点a和点b是否在同一个连通块中,a和b可能相等;
“Q2 a”,询问点a所在连通块中点的数量;
输入格式
第一行输入整数n和m。

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

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

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

每个结果占一行。

数据范围
1≤n,m≤105
输入样例:
5 5
C 1 2
Q1 1 2
Q2 1
C 2 5
Q2 5
输出样例:
Yes
2
3
*/

int n, m;

set<int> ss[100010];

int cnt[100010];

int main()
{
    cin >> n >> m;

    for (int i = 1; i <= n; i++) {
        ss[i].insert(i);
        cnt[i] = 1;
    }

    for (int i = 0; i < m; i++) {
        char op[10];
        cin >> op;
        if (op[0] == C) {
            int a, b;
            cin >> a >> b;

            while (a != *ss[a].begin()) {
                a = *ss[a].begin();
            }
            while (b != *ss[b].begin()) {
                b = *ss[b].begin();
            }

            if(a !=b){
                if(a < b){
                    cnt[a] += cnt[b];
                    ss[b].insert(a);
                }
                else{
                    cnt[b] += cnt[a];
                    ss[a].insert(b);
                }
            }
        }
        else if (op[0] == Q&& op[1] == 1) {
            int a; int b;
            cin >> a >> b;

            while (a != *ss[a].begin()) {
                a = *ss[a].begin();
            }
            while (b != *ss[b].begin()) {
                b = *ss[b].begin();
            }

            if (a == b) cout << "Yes" << endl;
            else cout << "No" << endl;

        }
        else if (op[0] == Q&& op[1] == 2) {
            int a;
            cin >> a;

            while (a != *ss[a].begin()) {
                a = *ss[a].begin();
            }
            cout << cnt[a] << endl;
        }
    }
}



作者:itdef
链接:https://www.acwing.com/solution/content/14781/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

算法2

正规并查集做法

同样的 相同集合元素个数的记录也是要注意

序号小的统计记录不重复的记录下之后的所有大序号元素的个数。

序号大的元素记录里不要统计小序号元素个数,避免重复。

 

C++ 代码

#include <iostream>


using namespace std;

const int N = 100010;

int n,m;
int p[N],siz[N];

int find(int x){
    if(p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int main()
{
    cin >> n >>m;
    for(int i = 1;i <= n;i++){
        p[i] = i;
        siz[i] = 1;
    }

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

        if(op == "C"){
            cin >> a >> b;
            a= find(a); b = find(b);
            if(a != b){
                p[a] = b;
                siz[b] += siz[a];
            }
        }else if( op == "Q1"){
            cin >> a>> b;
            if(find(a) == find(b)) cout << "Yes" << endl;
            else cout << "No"<< endl;
        }else{
            cin >> a;
            cout << siz[find(a)] << endl;
        }
    }



    return 0;
}



作者:itdef
链接:https://www.acwing.com/solution/content/14781/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

#include <iostream>#include <map>#include <set>using namespace std;

/*//并查集模板void init(int n){    for(int i=1;i<=n;i++)        fa[i]=i;}int get(int x){    return fa[x]==x?x:fa[x]=get(fa[x]);//路径压缩,防止链式结构}void merge(int x,int y){    fa[get(x)]=get(y);}
给定一个包含n个点(编号为1~n)的无向图,初始时图中没有边。
现在要进行m个操作,操作共有三种:
“C a b”,在点a和点b之间连一条边,a和b可能相等;“Q1 a b”,询问点a和点b是否在同一个连通块中,a和b可能相等;“Q2 a”,询问点a所在连通块中点的数量;输入格式第一行输入整数n和m。
接下来m行,每行包含一个操作指令,指令为“C a b”,“Q1 a b”或“Q2 a”中的一种。
输出格式对于每个询问指令”Q1 a b”,如果a和b在同一个连通块中,则输出“Yes”,否则输出“No”。
对于每个询问指令“Q2 a”,输出一个整数表示点a所在连通块中点的数量
每个结果占一行。
数据范围1≤n,m≤105输入样例:5 5C 1 2Q1 1 2Q2 1C 2 5Q2 5输出样例:Yes23*/
int n, m;
set<int> ss[100010];
int cnt[100010];
int main(){    cin >> n >> m;
    for (int i = 1; i <= n; i++) {        ss[i].insert(i);        cnt[i] = 1;    }
    for (int i = 0; i < m; i++) {        char op[10];        cin >> op;        if (op[0] == ‘C‘) {            int a, b;            cin >> a >> b;
            while (a != *ss[a].begin()) {                a = *ss[a].begin();            }            while (b != *ss[b].begin()) {                b = *ss[b].begin();            }
            if(a !=b){                if(a < b){                    cnt[a] += cnt[b];                    ss[b].insert(a);                }                else{                    cnt[b] += cnt[a];                    ss[a].insert(b);                }            }        }        else if (op[0] == ‘Q‘&& op[1] == ‘1‘) {            int a; int b;            cin >> a >> b;
            while (a != *ss[a].begin()) {                a = *ss[a].begin();            }            while (b != *ss[b].begin()) {                b = *ss[b].begin();            }
            if (a == b) cout << "Yes" << endl;            else cout << "No" << endl;
        }        else if (op[0] == ‘Q‘&& op[1] == ‘2‘) {            int a;            cin >> a;
            while (a != *ss[a].begin()) {                a = *ss[a].begin();            }            cout << cnt[a] << endl;        }    }}


作者:itdef链接:https://www.acwing.com/solution/content/14781/来源:AcWing著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

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

上一篇:Dns Over Https(DoH) --- Windows10 预览版 19645


下一篇:C# StreamWriter log batch message sync and async