P1892 [BOI2003]团伙

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,qn


思路:扩展域并查集的一个应用。只有两个关系(敌人和朋友),并查集维护的是关系的同时发生。数组开两倍大小。

注意题目并没有说朋友的敌人是我的敌人

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;
}
©️2020 CSDN 皮肤主题: 技术工厂 设计师:CSDN官方博客 返回首页