[caioj 1094] 并查集3(校园白社会)---带权并查集

题目描述

校园内有很多白社会团伙,他们专做好事。经过长期的卧底,学校有n个人,任何两个认识的人不是朋友就是敌人,而且满足:①我朋友的朋友是我的朋友;②我敌人的敌人是我的朋友。所有是朋友的人组成一个团伙。现在拥有关于这n个人的m条信息(即某两个人是朋友,或某两个人是敌人),请你计算出这个城市最多可能有多少个白社会团伙。
数据范围:2≤N≤2000,1≤M≤5000。
输入数据
第一行包含一个整数N,第二行包含一个整数M,接下来M行描述M条信息,内容为以下两者之一:“F x y”表示x与y是朋友;“E x y”表示x与y是敌人(1≤x≤y≤N)。
输出数据
包含一个整数,即可能的最大团伙数。
输入

输出
样例输入

6
4
E 1 4
F 3 5
F 4 6
E 1 2

样例输出

3

提示

这题很多人都被坑到了吧,其实朋友的敌人也算敌人的。。。

给一组数据吧:

4

3

E 2 3

F 2 1

E 1 4

输出:

2

不用谢我,我叫雷锋

来源

分析

本题与洛谷1892"团伙"相同,属于并查集类的题目.共有两种思路,一种是带权并查集,一种是反集(也称拆点并查集合应该是吧).本次只介绍其中一种:带权并查集:
新增一个数组w表示x与fa[x]之间的关系:1 为敌人;0 为朋友
在路径压缩与合并时维护权值

在计算团伙数时,只需在统计每个集合中的关系(每个集合最多有两个团伙,若是每个集合中的元素与根的w(关系)相同,则只有一个团伙)

代码

#include <cstdio>
#include <cstdlib>
#define open(s) freopen(s".in","r",stdin); freopen(s".out","w",stdout);
#define close fclose(stdin); fclose(stdout); 
using namespace std;

int n,m;
int fa[2005];
int w[2005];
bool f[2005][2];

inline int read()
{
    int k=1;
    int sum=0;
    char c=getchar();
    for(;'0'>c || c>'9' ;c=getchar())
        if(c=='-') k=-1;
    for(;'0'<=c && c<='9';c=getchar())
        sum=sum*10+c-'0';
    return sum*k;
}

inline void write(int x)
{
    if(x<0) { putchar('-'); x*=-1; }
    if(x>9) write(x/10);
    putchar(x%10+'0');
}

inline int find(int x)
{
    if(fa[x]==x) return x;
    int tmp=find(fa[x]);
    w[x]=(w[x]+w[fa[x]])%2;
    return fa[x]=tmp;
}

inline void join(int x,int y,int z)
{
    int x1=find(x),y1=find(y);
    if(x1==y1) return ;
    w[x1]=(z+w[y]-w[x]+2)%2;
    fa[x1]=y1;
}

int main()
{
    open("1094");

    n=read();
    m=read();

    for(int i=1;i<=n;++i)
        fa[i]=i;
    for(int i=1;i<=m;++i)
    {
        char c;
        scanf(" %c",&c);
        int x=read(),y=read(),k=(c=='E'?1:0);
        join(x,y,k);
    }
    int ans=0;
    for(int i=1;i<=n;++i)
        //if(fa[i]!=i)
    {
        int x=find(i);
        if(!f[x][w[i]])
        {
            f[x][w[i]]=1;
            ++ans;
        }
    }
    write(ans);
    close;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值