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;
}

posted @ 2020-07-26 16:17  Shadow_hyc  阅读(100)  评论(0)    收藏  举报