精讲并查集经典习题:P1892 [BOI2003]团伙(超详细)
一,需要开o2才能AC的代码
用emys[i]存储第i个人的敌人数,vector<long long>emyr[i]存储i个人的所以敌人,fa[i]存储第i个人的大boss。
1.初始化 把fa[i]全部设为i
2.在输入时如果u跟v是朋友,则join(u,v);否则说明是敌人,那么u,v的敌人数++,再各自把对方加入自己的敌人中
3.遍历所有人,如果某一人的敌人数>1,则让他的所有敌人两两组队
4.最后数一下有多少个大boss就能知道有多少个团体了。
代码:
#include <bits/stdc++.h>
using namespace std;
long long n,m,fa[1000001],emys[1000001],ans,u,v,a,b;
vector<long long>emyr[10001];
char t;
long long find(int x)
{if(fa[x] == x) return x;else return find(fa[x]);
}
void join(int a,int b)
{int x = find(a),y = find(b);if(x != y) fa[x] = y;
}
int main()
{scanf("%lld%lld",&n,&m);for(int i = 1; i <= n; i++) fa[i] = i;for(int i = 1; i <= m; i++){scanf("%s%lld%lld",&t,&u,&v);if(t == 'F') join(u,v);else{emys[u]++;emys[v]++;emyr[u].push_back(v);emyr[v].push_back(u);}}
// for(int i = 1; i <= n; i++) cout<<emys[i]<<" ";
// cout<<endl;
// for(int i = 1; i <= n; i++)
// {
// for(int j = 0; j < emyr[i].size(); j++)
// cout<<emyr[i][j]<<" ";
// cout<<endl;
// }for(int i = 1; i <= n; i++){if(emys[i] > 1){for(int j = 0; j < emys[i]; j++)for(int k = j + 1; k < emys[i]; k++)join(emyr[i][j],emyr[i][k]);}}for(int i = 1; i <= n; i++)if(fa[i] == i)ans++;printf("%lld",ans);return 0;
}
没开o2优化:
开了o2优化:
二,不用开o2优化也能AC的代码(1)
首先,要充分理解题目。 “敌人的敌人就是朋友”可以这么理解:如果一个人有两个或更多敌人,这些敌人就应该被合并。
代码中的emy数组就是记录了每个人的第一个敌人,再遇到敌人时就把这两个敌人合并。
其他的跟前面一样。
没想出这点的话还是蛮难做的。
#include <bits/stdc++.h>
using namespace std;
long long n,m,fa[1000001],emys[1000001],ans,u,v,a,b;
vector<long long>emyr[10001];
char t;
long long find(int x) //寻找x的大boss
{if(fa[x] == x) return x;else return find(fa[x]);
}
void join(int a,int b)//合并
{int x = find(a),y = find(b);if(x != y) fa[x] = y;
}
int main()
{scanf("%lld%lld",&n,&m);for(int i = 1; i <= n; i++) fa[i] = i;for(int i = 1; i <= m; i++){scanf("%s%lld%lld",&t,&u,&v);if(t == 'F') join(u,v);//是朋友就合并 else{if(emys[u] == 0) emys[u] = find(v);//一个人有两个或更多敌人,合并他们 else join(v,emys[u]);if(emys[v] == 0) emys[v] = find(u);else join(u,emys[v]);}}
// for(int i = 1; i <= n; i++) cout<<emys[i]<<" ";
// cout<<endl;
// for(int i = 1; i <= n; i++)
// {
// for(int j = 0; j < emyr[i].size(); j++)
// cout<<emyr[i][j]<<" ";
// cout<<endl;
// }
// for(int i = 1; i <= n; i++)
// {
// if(emys[i] > 1)
// {
// for(int j = 0; j < emys[i]; j++)
// for(int k = j + 1; k < emys[i]; k++)
// join(emyr[i][j],emyr[i][k]);
// }
// }for(int i = 1; i <= n; i++)if(fa[i] == i)ans++;printf("%lld",ans);return 0;
}
三,引入反集
a 表示 a 的朋友集,a + n 表示 a 的敌人集
1.如果 a 和 b 是朋友,那么 将 a 和 b 的朋友集 相连即可: p[find(a)] = find(b);
2/如果 a 和 b 是敌人,则 将 a 的敌人集与 b 的朋友集 相连,将 b 的敌人集与 a 的朋友集 相连,即: p[find(a + n)] = Find(b),p[find(b + n)] = find(a)
最后统计有多少团伙时,1 ~ n 中(朋友集)所有 p[i] == i 的人数就是答案。因为根据题意,所有团伙一定会分布在朋友集中,因此 只要在朋友集中统计没有祖先的节点个数即可。
代码(以下代码转载自洛谷 P1892 [BOI2003]团伙(并查集变种 反集)_Brightess的博客-CSDN博客):
#define _CRT_SECURE_NO_WARNINGS 1
#include <bits/stdc++.h>using namespace std;
//#define int long long
int n, m;
const int N = 1010;
int p[N << 1];int Find(int x)
{if (p[x] != x) p[x] = Find(p[x]);return p[x];
}signed main()
{int T = 1; //cin >> T;while (T--){cin >> n >> m;int res = 0;for (int i = 1; i < N<<1; ++i) p[i] = i;for (int i = 1; i <= m; ++i){char op[2]; int a, b;cin >> op >> a >> b;int pa = Find(a), pb = Find(b);int pan = Find(a + n), pbn = Find(b + n);if (*op == 'F'){p[pb] = pa;}else{p[pbn] = pa;p[pan] = pb;}}for (int i = 1; i <= n; ++i) if (p[i] == i) ++res;cout << res << '\n';}return 0;
}
精讲并查集经典习题:P1892 [BOI2003]团伙(超详细)相关推荐
- P1892 [BOI2003]团伙(反集)
P1892 [BOI2003]团伙 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题目描述 现在有 nn 个人,他们之间有两种关系:朋友和敌人.我们知道: 一个人的朋友的朋友是朋友 ...
- P1892 [BOI2003]团伙(并查集,反集)难度⭐⭐★
题目链接 反集 如果a和b是敌人,合并n+b和a,n+a和b 如果c和a是敌人,合并n+c和a,n+a和c 那么b和c自然就合并在一起了 这样就符合了题目敌人的敌人是朋友的规则 注意 并查集不要忘了初 ...
- How Many Answers Are Wrong HDU - 3038(带权并查集经典题,满满的都是注释)
How Many Answers Are Wrong HDU - 3038 点击打开链接 题意:现在有n个数(你并不知道这n个数是什么),m次查询,每次查询给出u,v,w.表示从第u个数到第v个数的 ...
- 【POJ - 1182】 食物链(附超详细讲解)(并查集--种类并查集经典题)
题干: 动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形.A吃B, B吃C,C吃A. 现有N个动物,以1-N编号.每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种. ...
- 洛谷 P1892 [BOI2003]团伙(并查集变种 反集)
[BOI2003]团伙 题目描述 现在有 n n n 个人,他们之间有两种关系:朋友和敌人.我们知道: 一个人的朋友的朋友是朋友 一个人的敌人的敌人是朋友 现在要对这些人进行组团.两个人在一个团体内当 ...
- 并查集经典应用之染色模型
3115. 疯狂的馒头 - AcWing题库 思路: 由于一个点最后的颜色取决于最后一次染色,那么我们可以倒着染色, 每染色一个点就将这个点删掉并记录颜色. 用ptr[i] 表示第i 个元素后面第一个 ...
- P1892 [BOI2003]团伙 +食物链 POJ - 1182 (并查集+思维)
思路①: 开数组enem[i]记录节点i的敌对节点,当再次输入i的敌对节点时就把他所在并查集的根节点和enem[i]并起来. #include<bits/stdc++.h> using n ...
- 洛谷P1892 [BOI2003]团伙
链接:P1892 题目描述 1920年的芝加哥,出现了一群强盗.如果两个强盗遇上了,那么他们要么是朋友,要么是敌人.而且有一点是肯定的,就是: 我朋友的朋友是我的朋友: 我敌人的敌人也是我的朋友. 两 ...
- BZOJ1370洛谷P1892 [BOI2003]团伙
并查集 我们将a的敌人定义为a+n,b的敌人的定义为b+n,所以a与b+n是一类人,b与a+n是一类人,然后就这么合并就好了 代码 //By AcerMo #include<cmath> ...
最新文章
- 802.11 MESH WLANs
- torch模拟sigmoid
- 2022 年营销自动化七大趋势前瞻
- u盘linux软件下载,u盘linux制作工具(Universal USB Installer)
- 工具_HBuilder使用快捷方式
- Android之使用AlertDialog.Builder类创建带列表的对话框和带自己所布局视图的对话框
- Swift仿写喜马拉雅FM
- python题目训练(随时更新)
- JAVAWeb使用POI做导出Excel
- 进度计划中的时间相关术语
- matlab运行出现:Optimization terminated.
- Python自动生成新闻报告
- 计算机与电气工程sci期刊,电气工程专业什么SCI期刊比较好
- jpg图片太大怎么压缩变小
- 福建中医药大学数字化校园应用及数据容灾系统181万
- windows杀死进程
- 堆垛实训报告总结_叉车实训总结
- post请求302以及post请求变更为get请求的问题排查小记
- 如何将C 项目部署到云服务器上,如何将C 应用程序放在云服务器上
- python:print函数打印空行