一,需要开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;
}