• 并查集 2019年8月10日计蒜客联盟周赛 K.数组


    题目链接:https://nanti.jisuanke.com/t/40860

    题意:给一个长度为n的数组a[],n<1e5,a[i]<1e5

    三个操作:

    1 x y:把所有值为x的数据改成a[y]

    2 x:输出a[x]的值

    3 x:值为x的树有多少个

    官方解析:

    设father[i]表示初值为i的元素,当前的值为多少。

    设cnt[i]表示当前值为i的元素有多少。

    用并查集维护这两个数组。

    个人思路:

    做这道题的人并不多,可能是想不到可以用并查集(那我+1)。

    因为要将值为x的数据进行修改,所以fa[i]!=a[i],而是存的i,即fa[i]=i(i从1到maxn)。

    1操作把x和a[y]进行merge(merge中把x的size加给a[y],x的size置为0,变成有size[x]+size[a[y]]个a[y],0个x)。

    但是如果size[x]已经是0了(有0个值为x的数,已经变成某个数tmp了),就不能把他和a[y]合并了,不然2操作查询a[x]就会输出是a[y]而不是tmp。【感谢starhai霸霸的指出】

    2操作输出a[x]的祖宗结点,3操作输出size[x]。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=100000;
    int n,a[maxn];
    int size[maxn],fa[maxn];
    int get(int x)
    {
        if(fa[x]==x){return x;}
        int y=fa[x];
        fa[x]=get(y);
        return fa[x];
    }
    
    void merge(int a,int b)
    {
        a=get(a);
        b=get(b);
        if(a!=b)
        {
            fa[a]=b;
            size[b]+=size[a];
            size[a]=0;
        }
    }
    
    int main()
    {
        scanf("%d",&n); 
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            fa[a[i]]=a[i];
            size[a[i]]++;
        }
        int q;
        scanf("%d",&q);
        while(q--)
        {
            int op,x,y;
            scanf("%d",&op);
            if(op==1)
            {
                scanf("%d%d",&x,&y);
                if(size[x]) merge(x,a[y]);
            }
            else if(op==2)
            {
                scanf("%d",&x);
                printf("%d
    ",get(a[x]));
            }
            else
            {
                scanf("%d",&x);
                printf("%d
    ",size[x]);
            }
        }
        return 0;    
    }
    View Code

    自用带权并查集模板

    #include <iostream>
    using namespace std;
    int father[110],n;
    int dist[110],size[110];
    void init()
    {
        for(int i=1;i<=n;i++)
        {
            father[i]=i,dist[i]=0,size[i]=1;
        }
    }
    int get(int x){
        if(father[x]==x){return x;}
        int y=father[x];
        father[x]=get(y);
        dist[x]+=dist[y];
        return father[x];
    }
    void merge(int a,int b)
    {
        a=get(a);
        b=get(b);
        if(a!=b)
        {    
            father[a]=b;
            dist[a]=size[b];
            size[b]+=size[a];
        }
    }
    
    int main() {
        n=10;init();  //每个节点刚开始的祖宗都是自己
        merge(1,2);  //1的根节点指向2的根节点,father[1的祖宗]=2的祖宗
        merge(10,7);
        merge(3,4);
        merge(3,7);
        get(1);  //找出1的祖宗
        cout<<dist[1]+1<<endl;  //size是包括自己的子孙个数,dist为元素到队首的距离
        get(3);
        cout<<dist[3]+1<<endl;
        return 0;
    }
    View Code

    dist跟merge的顺序有关

  • 相关阅读:
    我的算法日志:数据结构之顺序队列与循环队列
    我的算法日志:排序算法之快速排序
    算法:冒泡排序
    算法:桶排序(简易版)
    Android:配置LitePal 3.0
    Android:简单粗暴的二维码生成与扫描
    Linux
    Python
    Linux
    Python
  • 原文地址:https://www.cnblogs.com/myrtle/p/11337546.html
Copyright © 2020-2023  润新知