https://www.luogu.com.cn/problem/P1892
题目描述
给定 n 个人,他们之间有两个种关系,朋友与敌对。可以肯定的是:
- 与我的朋友是朋友的人是我的朋友
- 与我敌对的人有敌对关系的人是我的朋友
现在这 nn 个人进行组团,两个人在一个团队内当且仅当他们是朋友。
求最多的团体数。
输入格式
第一行一个整数 n 代表人数。
第二行一个整数 m 代表每个人之间的关系。
接下来 m 行每行一个字符 optopt 与两个整数 p,qp,q
- 如果 optopt 为
F
代表 pp 与 qq 为朋友。 - 如果 optopt 为
E
代表 pp 与 qq 为敌人。
输出格式
一行一个整数代表最多的团体数。
输入输出样例
输入 #1复制
6 4 E 1 4 F 3 5 F 4 6 E 1 2
输出 #1复制
3
说明/提示
对于 100\%100% 的数据,2 \le n \le 10002≤n≤1000,1 \le m \le 50001≤m≤5000,1 \le p,q \le n1≤p,q≤n。
思路:扩展域并查集的一个应用。只有两个关系(敌人和朋友),并查集维护的是关系的同时发生。数组开两倍大小。
注意题目并没有说朋友的敌人是我的敌人
p[i:(1~n)]表示 i的朋友
p[i:(n+1)~2n)] 表示i的敌人
- 与我的朋友是朋友的人是我的朋友 p[find(x)]=find(y);
- 与我敌对的人有敌对关系的人是我的朋友
- p[find(x)]=find(y+n); (单纯一句够了嘛?) //这句话表示了:y的敌人是x的朋友,维护这种关系的发生
- 由于敌人关系是相对的,并查集维护的是相同的关系
- 但是同时 x的敌人也是y的朋友
- 所以还要p[find(y)]=find(x+n);
- 并查集维护这两个关系的同时发生
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=1e5;
typedef long long LL;
LL p[maxn*2];//x+n表示x的敌人
LL vis[maxn];
LL find(LL x)
{
if(x!=p[x])
p[x]=find(p[x]);
return p[x];
}
int main(void)
{
cin.tie(0);std::ios::sync_with_stdio(false);
LL n,m;cin>>n>>m;
for(LL i=1;i<=n*2;i++) p[i]=i;
char op[10];LL x,y;
while(m--)
{
cin>>op>>x>>y;
if(op[0]=='F') //维护朋友
{
p[find(x)]=find(y);//与我的朋友是朋友的人是我的朋友
}
else if(op[0]=='E')
{
p[find(x)]=find(y+n);//与我敌对的人有敌对关系的人是我的朋友
p[find(y)]=find(x+n);//交换主元
}
}
LL ans=0;
for(LL i=1;i<=n;i++)
{
if(!vis[find(i)])
{
vis[find(i)]=1;ans++;
}
}
cout<<ans<<endl;
return 0;
}