题目
https://pintia.cn/problem-sets/994805342720868352/problems/994805354108403712
题意
一幅画里的鸟都是同一棵树上的,输出树和鸟的数量,并判断给定的两只鸟是否在同一棵树上
代码解析
本题运用了并查集,但有以下几点需要注意:
首先我需要解释一下这行代码:else return pre[x]=find(pre[x]);
这句是先把find到的x赋值给pre[x]的,查找路径上的每个点,下一次找的时候,就能直接找到终点了;再解释一下就是因为此函数找到根节点就返回,所以所有的节点在递归的赋值中最终都指向了根节点,为后续的Find节约了时间
其次是这行代码:int t=find(i);
,如果两幅画有重合部分,那这两幅画的鸟应该都属于同一颗树,这行代码就是把同一棵树的子树合并起来(必须有)
最后,测试点给出的1到某个整数的节点并不一定是连续的,比如有可能是1 2 3 6 8,就跳过了4 5 7,所以后面的判断的基础就是这个数字要存在(vis[i])。虽然题目中有continuously,但不加vis好像就是错的,还是加一下比较好
AC代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=10005;
int pre[maxn];
unordered_map<int,int> vis;
int find(int x)
{
if(x==pre[x]) return x;
else return pre[x]=find(pre[x]);//路径压缩
}
void combine(int a,int b)
{
int x=find(a),y=find(b);
if(x!=y) pre[y]=x;
}
int main()
{
int n,m,k,x,y;
for(int i=1;i<=maxn;i++) pre[i]=i;
cin>>n;
while(n--)
{
cin>>m>>x;
vis[x]=1;
for(int i=0;i<m-1;i++)
{
cin>>y;
vis[y]=1;
combine(x,y);
}
}
int cnt1=0,cnt2=0;
for(int i=1;i<=maxn;i++)
{
if(vis[i])
{
cnt2++;
int t=find(i);//这步必须有,这是为了合并同一棵树的子树
}
}
for(int i=1;i<=maxn;i++)
{
if(vis[i])
{
if(pre[i]==i) cnt1++;
}
}
cout<<cnt1<<" "<<cnt2<<endl;
cin>>k;
while(k--)
{
cin>>x>>y;
if(find(x)==find(y)) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
}