Luogu P1892 [BOI2003]团伙


思路
这道题其实操作就是普通并查集的基础合并和查询操作,只是要稍稍思考一下对于 “ 我朋友的朋友是我的朋友,我敌人的敌人也是我的朋友 ” 这两句话的处理方法。
很明显的是,对于第一句话,“我朋友的朋友是我的朋友”,是很好操作的。如果读入数据告诉我们x和y这两个强盗是朋友,那么我们就把x和y所在的集合合并起来就可以了(两个集合里一旦分别有两个
人是朋友,那么这两个集合里的所有人都是朋友)。
那么考虑如何对 “ 我敌人的敌人也是我的朋友 ” 这句话进行操作。我们可以考虑用一个数组e存储每个点的第一个敌人是谁,因为如果同一个点有一个或者多个敌人,那么这些敌人就应该被合并。然后就
是在每次读入敌人时判断当前点是否遇到第一个敌人,若没有,则赋值,若有,则合并该点第一次遇到的敌人和刚刚读入的这个点的敌人。
Code
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define MAXN 1010
int n, m, f[MAXN], res;
int e[MAXN];//e数组存储每个点遇到的第1个敌人
inline int get_father(int k){
return k == f[k] ? k : f[k] = get_father(f[k]);
}//查询函数
inline void merge(int x,int y){
int p = get_father(x), q = get_father(y);
f[p] = q;
return;
}//合并函数(这里用的是路径压缩)
int main(){
scanf("%d%d", &n, &m);
for (int i = 1; i <= n;++i)
f[i] = i;//初始化
for (int i = 1; i <= m;++i){
char opt[5];
int x, y;
scanf("%s", opt);
scanf("%d%d", &x, &y);
if(opt[0]=='F')
merge(x, y);//若是朋友直接合并
else{
if(e[x]==0)//若没有遇到第一个敌人,赋值
e[x] = get_father(y);
else merge(y, e[x]);//否则将该点遇到的第一个敌人和当前读入的敌人合并
if(e[y]==0)
e[y] = get_father(x);
else merge(x, e[y]);//同上,对两个点分别操作,因为他们互为敌人
}
}
for (int i = 1; i <= n;++i)
if(f[i]==i) ++res;//有几个根就有几个团伙
printf("%d\n", res);
return 0;
}

浙公网安备 33010602011771号