1.题目引入:
给定一个包含 nn 个点(编号为 1∼n1∼n)的无向图,初始时图中没有边。
现在要进行 mm 个操作,操作共有三种:
C a b
,在点 aa 和点 bb 之间连一条边,aa 和 bb 可能相等;Q1 a b
,询问点 aa 和点 bb 是否在同一个连通块中,aa 和 bb 可能相等;Q2 a
,询问点 aa 所在连通块中点的数量;输入格式
第一行输入整数 nn 和 mm。
接下来 mm 行,每行包含一个操作指令,指令为
C a b
,Q1 a b
或Q2 a
中的一种。输出格式
对于每个询问指令
Q1 a b
,如果 aa 和 bb 在同一个连通块中,则输出Yes
,否则输出No
。对于每个询问指令
Q2 a
,输出一个整数表示点 aa 所在连通块中点的数量每个结果占一行。
数据范围
1≤n,m≤105
2.样例输出:
输入样例:
5 5 C 1 2 Q1 1 2 Q2 1 C 2 5 Q2 5
输出样例:
Yes 2 3
这道题是并查集模板题这里不过多赘述,这里有两点需要注意:
1、合并和加和时注意方向
2、 /* 下面注意不能写成 :
if(find(b)==find(c))
{
f[find(b)]=find(c);
su[find(c)]+=su[find(b)];
}
这主要是因为它先合并再加的缘故,因为我们用这种方式加和
其实只有祖先被赋值为 1,因此他们在一个连通集内无外乎=1+1
这显然是错误的 ,这里极易出错。
*/
3.代码如下:
#include<iostream> #include<cstring> #include<string> #include<algorithm> using namespace std; int n,m,b,c; const int maxn=1e5+10; int f[maxn],su[maxn]; string a; int find(int x) { if(f[x]==x) return f[x]; return f[x]=find(f[x]); } int main() { cin>>n>>m; for(int i=1;i<=n;i++) { f[i]=i; su[i]=1; } while(m--) { cin>>a; if(a=="C") { cin>>b>>c; b=find(b); c=find(c); if(b!=c) { f[b]=c; su[c]+=su[b]; } /* 这里注意不能写成 : if(find(b)==find(c)) { f[find(b)]=find(c); su[find(c)]+=su[find(b)]; } 这主要是因为它先合并再加的缘故,因为我们用这种方式加和 其实只有祖先被赋值为 1,因此他们在一个连通集内无外乎=1+1 这显然是错误的 */ } else if(a=="Q1") { cin>>b>>c; if(find(b)==find(c)) { puts("Yes"); } else { puts("No"); } } else { cin>>b; cout<<su[find(b)]<<endl; } } return 0; }