在并查集问题的处理中有几种常见的实用方法,下面介绍第一种:虚空节点(我这么叫,莫黑)。
结合例题身体好~
洛谷oj 10.19比赛题目 宗教信仰 religion
题目描述 Description
在一片大陆上有许多个国家和许多个宗教。每一个国家在某一时刻仅有1个官方信仰的宗教。
这些宗教可能会互相兼并,合成一个宗教。某个国家也可能在某一时刻改信一个宗教。
现在给出宗教兼并和国家改宗的大事表,以及若干个询问,询问在当前时刻2个国家是否信仰一个宗教。
每个国家在开始时,都信仰着不同的宗教。
输入输出格式 Input/output
输入格式:
第一行 1 个整数 N 表示国家数量。
第二行 1 个整数 Q 表示一共有Q个大事及询问。
以下 Q 行 每行形式如下:
1 A B 表示 A 国所信仰的宗教与 B 国所信仰的宗教 合并为 1 个宗教;
2 A B 表示 A 国改为信仰 B 国所信仰的宗教;
3 A B 表示查询 A 国 和 B 国 现在是否信仰同一个宗教;
//如果 1 2 3信仰A宗教,4 5信仰B宗教,那么执行2 1 4 后,2 3国信仰A宗教,1 4 5国信
//仰B宗教
输出格式:
对于每一个 询问 输出 ‘Yes‘ 或 ‘No‘ 来回答询问,分别表示是同一个宗教或不是同一个宗教.
input
5
6
1 3 4
1 2 4
3 2 4
2 5 2
3 1 5
3 3 5
output
Yes
No
Yes
题目链接:http://www.luogu.org/problem/show?pid=T105
好了,分析一下,显然是并查集对吧,那么 3操作是查询,1操作路径压缩合并,2吗,是什么呢。。。。。。
我们很快会想到:如果只改这么一个点的话,那么如果这是个根(老祖宗,路径压缩后的根节点),那么我一改,岂不难办了?
这样好了,我们每一个点刚开始的时候都把他父亲设成一个根本不存在的点好了~~这样一来原来的根就变成了现在我们的某虚空节点的一个儿子了,只要合并的时候只把虚空节点的儿子接到别处就好了,2333
所以就这么A了,可能讲不大明白,看代码一定能弄明白。
下面是某弱逼的弱渣程序:
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 int n,m; 5 int fa[2000500]; 6 int find(int x) 7 { 8 if(fa[x]==x)return x; 9 else return fa[x]=find(fa[x]); 10 } 11 int main() 12 { 13 cin>>n>>m; 14 for(int i=1;i<=n;i++) 15 { 16 fa[i]=i+n; 17 fa[i+n]=i+n; 18 } 19 while(m--) 20 { 21 int x,y,p; 22 scanf("%d%d%d",&p,&x,&y); 23 if(p==1) 24 { 25 int f1=find(x); 26 int f2=find(y); 27 fa[f1]=f2; 28 } 29 else if(p==2) 30 { 31 fa[x]=find(y); 32 } 33 else 34 { 35 int f1=find(x),f2=find(y); 36 if(f1==f2)cout<<"Yes"<<endl; 37 else cout<<"No"<<endl; 38 } 39 } 40 return 0; 41 }