从一道题引入 题目链接
题目描述
——你要是愿意,我就永远存在
某人的朋友圈实在是过于庞大且复杂,要判断两个人是不是朋友,那还真不容易。
现给出某个朋友圈关系图,求任意给出的两个人是否是朋友。
规定:如果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代码,学学别人是怎么写的,看看高手和自己到底都有哪些不同,把好的地方都学(偷)过来。