并查集与快读快写

从一道题引入 题目链接

题目描述

——你要是愿意,我就永远存在

某人的朋友圈实在是过于庞大且复杂,要判断两个人是不是朋友,那还真不容易。

现给出某个朋友圈关系图,求任意给出的两个人是否是朋友。

规定:如果x和y是朋友,y和z是朋友,那么x和z也是朋友。

如果x和y是朋友,那么x的朋友都是y的朋友,y的朋友也都是x的朋友。

输入描述:

第一行,三个整数n,m,p,(n ≤ 50000,m ≤ 50000,p≤50000),分别表示有n个人,m个朋友关系,询问p对朋友关系。

以下m行:每行两个数Mi, Mj,1 ≤ Mi, Mj ≤ n,表示Mi和Mj具有朋友关系。

接下来p行:每行两个数Pi ,Pj,询问Pi,Pj是否具有盆友关系

输出描述:

P行,每行一个“Yes”或“No”(不包含引号)。表示第i个询问的答案为“具有”或“不具有”朋友关系。
并查集顾名思义就是要对集合进行查找与合并

输入样例:

6 5 3
1 2
1 5
3 4
5 2
1 3
1 4
2 3
5 6

输出样例:

Yes
Yes
No

这是一道并查集板子题。
并查集顾名思义就是对集合进行查找与合并,主要有两个函数。

查找函数:

一般写法

int find(int root)
{
    int tmp,son;
    son=root;
    while(root!=pre[root])//一直搜到根节点出现
    {
       root=pre[root];
    }
    //root已经为根节点
    while(son!=root)//路径压缩
    {
       tmp=pre[son];
       pre[son]=root;//将属于同一集合的元素父节点统一为根节点
       son=tmp;
    }
    return root;//找到根节点
}

我在赛后查看了耗时较短的代码,发现原来查找函数还能够进行优化。

优化写法

int find(int root)
{
    return root==pre[root]?root:pre[root]=find(pre[root]);
}

在这里插入图片描述
这样稍微快了一点~

合并函数:

void  setu(int x,int y)
{
    int a=xfind(x),b=xfind(y);//找出有联系的两元素的各自根节点
    if(a!=b)//如果不相同
        pre[a]=b;//直接合并
}

接下来附上本题AC代码:

#include<bits/stdc++.h>
using namespace std;
int pre[50005];
int xfind(int root)
{
    return root==pre[root]?root:pre[root]=xfind(pre[root]);
}
void  setu(int x,int y)
{
    int a=xfind(x),b=xfind(y);
    if(a!=b)
        pre[a]=b;
}
int main()
{
    int n,m,p;
    cin>>n>>m>>p;
    for(int i=1;i<=n;i++)
    {
      pre[i]=i;
    }
    while(m--)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        setu(a,b);
    }
    while(p--)
    {
        int a,b;
       scanf("%d%d",&a,&b);
        if(xfind(a)!=xfind(b))
           printf("No\n");
        else
            printf("Yes\n");
    }
return 0;
}

在这里插入图片描述
以上代码的时间已经非常短了,但是我在看了用时最短的dalao的代码后,我发现原来他还使用了快读快写。
下面我附上我修改后的代码:

#include<bits/stdc++.h>
using namespace std;
int pre[50005];
void get(int &x)//快读
{
    char c = getchar(); x = 0;
    while(c < '0' || c > '9') c = getchar();
    while(c <= '9' && c >= '0') x = x*10+c-48, c = getchar();
}
void put(int x)//快写
{
    int num = 0; char c[15];
    while(x) c[++num] = (x%10)+48, x /= 10;
    while(num) putchar(c[num--]);
    putchar('\n');
}
int xfind(int root)
{
    return root==pre[root]?root:pre[root]=xfind(pre[root]);
}
void  setu(int x,int y)
{
    int a=xfind(x),b=xfind(y);
    if(a!=b)
        pre[a]=b;
}
int main()
{
    int  n,m,p,a,b;
    get(n);
    get(m);
    get(p);
    for(int i=1;i<=n;i++)
    {
      pre[i]=i;
    }
    while(m--)
    {
        get(a);get(b);
        setu(a,b);
    }
    while(p--)
    {
       get(a);get(b);
        if(xfind(a)!=xfind(b))
           printf("No\n");
        else
            printf("Yes\n");
    }
return 0;
}

在这里插入图片描述
是不是快了一丢丢呢…

这次写的比较乱,主要就是给自己记个板子用。同时也明白了赛后可以多看看别人的AC代码,学学别人是怎么写的,看看高手和自己到底都有哪些不同,把好的地方都学(偷)过来。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值