• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • YouClaw
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

RomanLin

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

【种类并查集】洛谷 P1892 [BalticOI 2003] 团伙 (Day 2)

题目

https://www.luogu.com.cn/problem/P1892

题解

种类并查集又称为扩展域并查集,是并查集的一种扩展形式,主要用于处理多组对立关系或多种状态划分的问题。在处理种类并查集问题时,有多少种类,就得开几倍的空间,用于表示所有的种类。

由题目已知 \(n\) 个人,他们之间存在两种关系:朋友和敌人。且具备以下关系:

  • 一个人的朋友的朋友是朋友
  • 一个人的敌人的敌人是朋友

对于第 \(x\) 个人,不妨设其朋友的集合为 \(exdsu[x]\),敌人的集合为 \(exdsu[x+n]\)。可以画出其关系图为:
团伙1

若第 \(x\) 个人和第 \(y\) 个人是朋友关系,可以画出其关系图为:
团伙2

若第 \(x\) 个人和第 \(y\) 个人是敌人关系,可以画出其关系图为:
团伙3

因为敌人关系不好表示,所以可以将其转化为朋友关系,使用种类并查集将所有朋友关系的集合合并起来。那么根据上述图表关系:

  • 若第 \(x\) 个人和第 \(y\) 个人是朋友关系,根据朋友的朋友是朋友的关系,只需将 \(exdsu[x]\) 和 \(exdsu[x+n]\) 合并成一个集合;
  • 若第 \(x\) 个人和第 \(y\) 个人是敌人关系,根据敌人的敌人是朋友的关系,只需分别将 \(exdsu[x]\) 与 \(exdsu[y+n]\) 和 \(exdsu[x+n]\) 与 \(exdsu[y]\) 合并为一个集合。

值得注意的编码小细节是,可以将较小者作为代表元,方便计算出答案。

参考代码

#include<bits/stdc++.h>
using namespace std;

constexpr int N = 2001;// 1000 个人,每个人至多有 2 种类别(朋友/敌人),因此需要开 1000 * 2 的空间
int n, m, u, v;
char r;// 关系
int exdsu[N];// 种类并查集

// 找到代表元
int find(int x) {
    return x == exdsu[x] ? x : exdsu[x] = find(exdsu[x]);
}

// 合并集合
void merge(int x, int y) {// 将 y 作为代表元
    exdsu[find(x)] = find(y);
}

int main() {
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
    cin >> n >> m;
    iota(exdsu + 1, exdsu + 1 + n * 2, 1);
    while (m --) {
        cin >> r >> u >> v;
        if (r == 'F') {
            merge(u, v);
        } else {
            merge(v + n, u);
            merge(u + n, v);
        }
    }
    int ans = 0;
    for (int i = 1; i <= n; ++ i) {
        if (i == find(i)) {
            ++ ans;
        }
    }
    cout << ans;
    return 0;
}

posted on 2026-04-12 23:03  RomanLin  阅读(2)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3