• FJUT ACM 2144 并查集


    并查集

    TimeLimit:3000MS  MemoryLimit:128MB
    64-bit integer IO format:%lld
     
    Problem Description

    并查集详解:http://blog.csdn.net/dellaserss/article/details/7724401

    这里注意一下,用递归压缩路径的同学,可能会爆栈,从而导致

    Runtime Error 建议用循环压缩路径

     

    Input

    输入多组数据

    每组数据第一行是两个整数n(1<=n<=10^6),m(1<=m<=10^6)。分别表示元素数、操作数(初始时每个元素以自己为一个集合,元素编号是1-n)

    接下来m行,每行有如下几种输入:

    union x y ——表示将x所在的集合和y所在的集合合并为一个集合。

    same x y ——询问x和y是否为同一个集合、为同一个集合输出1,不同集合输出0

    num x ——询问x所在的集合共有多少个元素

    max x ——询问x所在的集合中元素编号最大是多少

    setnum ——询问现在总共有多少个集合

    Output

    对于每个same、num、max、setnum操作输出一行,用一个整数表示答案。

    SampleInput
    5 10
    setnum
    same 1 2
    union 1 2
    same 1 2
    union 2 3
    same 1 3
    union 4 5
    setnum
    max 1
    num 4
    SampleOutput
    5
    0
    1
    1
    2
    3
    2
    [思路]:因为这是一题并差集进行多种操作得到题目,期间涉及连接集合,查询集合最大值,查询集合元素,输出现在有几个集合的操作
    ,我的一开始的思路是建立一个并查集,在连接过程进行压缩路径,防止并查集退化至o(n),而压缩路径就是把多个子节点连接到一个父
    节点上,
    如图:


    然后我一开始的思路在寻找集合的元素最大值,与寻找元素的个数,进行了2次O(n)的查询,结果是TLE,然后看了数据后,最坏的情况有10的6次方个元素,

    进行10的6次方次查询,那o(n)的复杂度必定TLE,经过CWL学长的提醒,必须用o(1)的查询,不然是无法通过的

    于是我改了下思路,在连接时,将集合的最大值连接为父节点,这样的话,每次查询只要查询集合的代表元素,即集合的父节点(最大值),

    就可以了,进行O(1)的查询

    而元素的值,我用了数组来做,有点类似前缀和+数组标记的思想,就是先把数组内的元素所对应的值置为1,再进行累加操作,因为每个节点的连接,

    必然把每个节点所代表的集合的元素相加,就可以得到结果!

    要注意一点:再进行元素集合个数的查询的时侯,要注意数据有很恶心的,就是把同个集合的元素进行连接,这样很容易导致错误。

    还有注意点:不要使用cin,因为太慢了,同样的算法,cin actime:1000ms+  ;scanf actime:300ms+;

    贴上代码的核心:

      1 /**
      2 rid: 157411
      3 user: 136155330
      4 time: 2018-02-03 17:46:19
      5 result: Accepted 
      6 */
      7 #include<stdio.h>
      8 #include<string.h>
      9 #define MAXN 1000005
     10 int pre[MAXN];///这个是并差集的指向
     11 int flags[MAXN];///求和
     12 int unionsearch(int root)
     13 {
     14     int son,tmp;
     15     son=root;
     16     while(root!=pre[root])///寻找父节点,一层一层向上
     17     {
     18      root=pre[root];
     19     }
     20     while(root!=son)///路径压缩,就是先将原上级保持在tmp内,在把pre[son]指向父节点,然后再将tmp赋值给son
     21     {tmp=pre[son];
     22     pre[son]=root;///这个过程会不断的去压缩路径,来优化,防止算法退化为O(N)的查找
     23     son=tmp;
     24     }///其实可以以递归写
     25     /**
     26     来至CWL学长的代码:
     27     int acfind(int x)
     28     {
     29     return pre[x]==x?x:pre[x]=acfind(pre[x]);
     30     }
     31     **/
     32     return root;
     33 }
     34 int unionnum(int x)
     35 {
     36     printf("%d
    ",flags[unionsearch(x)]);
     37 }///通过unionsearch查找最大值的父节点,然后输出标记值
     38 int unionmax(int x)
     39 {
     40     unionsearch(x);
     41     printf("%d
    ",pre[x]);
     42 }///unionsearch(x):主要是压缩路径,保证路径连接最大值,因为我的max值是在父节点
     43 int unionjoin(int x,int y)
     44 {
     45 int a;
     46 int b;
     47 a=unionsearch(x);
     48 b=unionsearch(y);///任何两个数去链接,得到的必然是大的值为父节点
     49 if(a!=b)
     50     {
     51         if(a>b)
     52             {pre[b]=a;
     53             flags[a]+=flags[b];
     54             }
     55         else if(a<b)
     56             {pre[a]=b;
     57             flags[b]+=flags[a];
     58             }///把最大值放到父节点去。因为每次进行连接,都会去选择最大值的节点进行连接
     59     }///因为flags初始化为1,只要每次连接都累加就可以,得到数字的和
     60 }
    
    
  • 相关阅读:
    利用runtime检測这个对象是否存在某属性?
    Android Studio 使用 SVN 必然遇到问题:commit ** File out of data 问题解决方法
    JPA測试实例
    pat(A) 1063. Set Similarity(STL)
    @Override用在哪儿
    Highcharts数据表示(3)
    #pragma pack (n) 惹的祸
    C++二阶构造函数
    使用自定义的控件
    C++ explicit
  • 原文地址:https://www.cnblogs.com/qq136155330/p/8413103.html
Copyright © 2020-2023  润新知