NHIP(2015)解题报告(第3题)

NHIP2015)解题报告(第3题)

第三题:树(tree

[题意分析]

     其实考的是如何合并集和,合并集合有两种方法:

方法一:

时间复杂度:o(2n)

Id

1

2

3

4

5

6

7

D

1

2

3

4

5

6

7

当输入(1,2)时:

Id

1

2

3

4

5

6

7

D

1

2  1

3

4

5

6

7

(1)找出12

(2)判断1,2的“祖先”是否相同。

      a.相同的,就把边数加1

      b.不相同,就把大的那个“祖先”换成小的那个“祖先”,

        在寻找出更原来大的“祖先”相同的,都变成小的那个祖先。

但此题的数据范围过大,这种方法会超时,不加以使用。

方法二:

时间复杂度:o(1)

根据并查集的思路,即可解决这道题:

NHIP(2015)解题报告(第3题) - lyc405503 - 梁懿琛的博客

 

NHIP(2015)解题报告(第3题) - lyc405503 - 梁懿琛的博客

 

这样可以提高效率,因此使程序不易超时:

NHIP(2015)解题报告(第3题) - lyc405503 - 梁懿琛的博客

  

[程序参考]

#include <iostream>

#include <fstream>

using namespace std;

ifstream fin ("tree.in");

ofstream fout ("tree.out");

#define cin fin

#define cout fout

const int mx=1000001;

int pt[mx],fa[mx],line[mx],ans=0;

int m,n,fx,fy,x,y;

 

int u_f(int x)

{

     int root=x;  

     while (fa[root]!=root)

       root=fa[root];   //顺藤摸瓜,找x的最前面的祖先;

    

     while (fa[x]!=x)  //xroot这条藤上的所有点的祖先都置为root

     {

         int t=fa[x];  //tx的父节点;

         fa[x]=root;   //x的父节点的祖先置为root

         x=t;  //再将x置为他的父节点,然后再从父节点为出发点,重置;

     } 

     return root;  //返回祖先节点;

}

 

 

int main()

{

     int i;

     cin>>m>>n;

    

     for (i=1;i<=m;i++)   //初始化每个点

     {

       fa[i]=i;   //i个点的父亲就是他自己,

       pt[i]=1;   //作为1个“树”——它包含1个节点(它自己)

       line[i]=0; //0条边

    }

    

     for (i=0;i<n;i++)   //输入n条边,通过并查集,找每个点所在的集合,以及每个集合的点数边数

     {

       cin>>x>>y;

      

       fx=u_f(x);    //x点的祖先

       fy=u_f(y);    //y点的祖先

      

       if (fx!=fy)   //加入这条边上的两点的祖先各不相同,

       {

         fa[fy]=fx;  //则将y点的祖先置为x点的祖先;

         line[fx]+=1+line[fy];  //x点所在集合的边数=x点所在集合的边数+y点所在集合的边数+1

         pt[fx]+=pt[fy];   //x点所在集合的节点数=x点所在集合的节点数+y点所在集合的节点数

       } else line[fx]++;    //如果x点祖先和y点祖先相同,则x点和y点在同一集合,则该集合多出1条边;

    }

   

    for (i=1;i<=m;i++)   //统计m个节点中,祖先是自己并且边数不为1的祖先个数即为不同集合的数目

      if (fa[i]==i  && pt[i]==line[i]+1) ans++;

    cout<<ans<<endl;

     return 0;

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值