★实验任务
A国有 N个城市,这些城市编号为 1到 N,有一天,他们调查出恐怖分子在每个城市中都安放了炸弹,于是他们给炸弹也编上了序号,第 i 个城市里的炸弹编号为 i。现在他们想把这些炸弹转移,以便于销毁炸弹。
由于炸弹是通过不同人转移的,所以需要一个指挥部门来记录转移炸弹的信息,以便于有些人要查询这些信息。我们有两个操作:
1.将 a 炸弹目前所在城市中所有的炸弹转移到 b 炸弹所在的城市。
2.询问 a 炸弹目前在哪个城市编号和这个城市中有炸弹个数。
★数据输入
输入第一行包含两个数 N,Q(1<=N<=500000 , 1<=Q<=120000)。分别表示城市的个数和操作数。
接下来有 Q 行,每行表示一个操作,第一种操作输入格式为 1 a b,第二种操作输入格式为 2 a。(1<=a,b<=N)
★数据输出
对于第一个操作,如果两个炸弹在同一个城市里,输出“ERROR”,并不执行此操作。否则执行操作并不输出任何东西。
对于第二种操作,输出一行两个数表示炸弹所在的城市编号和该城市中炸弹个数,用一个空格分开。
输入示例1
3 3
1 1 2
1 3 2
2 2
输出示例1
2 3
输入示例2
3 5
1 1 2
1 2 1
2 1
1 1 3
2 1
输出示例2
ERROR
2 2
3 3
思路:本题用到并查集的思想,将题目给的两个操作看作是并和查,炸弹作为集合,初始时每个炸弹都属于自己的集合。
代码:
#pragma GCC optimize(2) //开O2优化
#include <cstdio>
#define MAX 500000
int Boomb[MAX];
//初始化为-1
void BoombInit(int size)
{
for(int i=1; i<=size; i++){
Boomb[i] = -1;
}
return ;
}
//查找x所在的城市
int UFFind(int root)
{
if(Boomb[root]<0) //如果父结点小于零 那么说明 已经是根节点 那么根就在自己
return root;
int son, tmp;
son = root;
while(Boomb[root]>=0){ //寻找城市
root = Boomb[root];
}
//路径压缩 再从最开始到根
while(son!=root){
tmp = Boomb[son]; //原本的上级
Boomb[son] = root; //将上级直接指向root
son = tmp;
}
return root; //返回树根
}
//a合并到b
void UFUnion(int a, int b)
{
a = UFFind(a); //找这俩的爸爸 如果相等 就返回
b = UFFind(b);
if(a==b){
printf("ERROR\n");
return ; //已经在同一座城市里
}
Boomb[b]+=Boomb[a]; //此时b要并到a上 那么a 要加上b中的炸弹个数
Boomb[a] = b; //然后再将b的父亲改为a
return ; //成功合并
}
int main(void)
{
int N, Q;
int a, b, instruct;
scanf("%d %d", &N, &Q);
BoombInit(N);
while(Q--){
scanf("%d", &instruct);
if(instruct==1){
scanf("%d %d", &a, &b);
UFUnion(a, b);
}
else{
scanf("%d", &a);
int tmp = UFFind(a);
printf("%d %d\n", tmp, -Boomb[tmp]);
}
}
return 0;
}