关于反集
如果a和b是敌人,合并n+b和a,n+a和b
如果c和a是敌人,合并n+c和a,n+a和c
那么b和c就并在一起了
这样就符合了题目敌人的敌人是朋友的规则
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int N = 2500;
int p[N];
int n,m;
int find(int x){
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
int main(){
cin>>n>>m;
for(int i=1;i<=2*n;i++) p[i] = i;
char op;
int a,b;
while(m--){
cin>>op>>a>>b;
if(op=='E'){
p[find(a+n)] = find(b);
p[find(b+n)] = find(a);
}else{
p[find(a)] = find(b);
}
}
int ans = 0;
for(int i=1;i<=n;i++) if(p[i] == i) ans++;
cout<<ans<<endl;
return 0;
}
这个问题描述的是一种图论中的算法,用于处理社交网络中的“朋友”和“敌人”关系。在这个问题中,我们有一个社交网络,其中包含了一些个体(用a、b、c等表示),以及他们之间的关系(朋友或敌人)。题目中提到的“合并”操作实际上是在构建一个等价类,即将具有相同关系状态的个体归为一类。
这里的“n”代表一个新加入的个体,它与已有的个体a、b、c等没有直接的关系。通过将n与a、b、c等个体合并,实际上是在创建一个新的关系集合,这个集合包含了n与a、b、c等个体的关系。通过这种方式,我们可以重新定义个体之间的关系,使得原本的敌人关系转变为朋友关系。
具体来说,这个过程可以这样理解:
初始状态:a和b是敌人,c和a是敌人。
合并操作:
将n与b合并,形成n+b,这意味着n和b现在被视为一个整体。
将n+b与a合并,形成一个新的关系集合,这个集合包含了n、b和a的关系。
同样,将n与a合并,形成n+a,然后将n+a与c合并,形成一个新的关系集合,这个集合包含了n、a和c的关系。
结果:通过上述合并操作,b和c现在都与n有直接的关系,因此它们之间也形成了直接的关系。由于b和c都是n的朋友(因为它们与n合并了),根据“敌人的敌人是朋友”的规则,b和c现在也成为了朋友。
这个算法的核心思想是,通过引入一个没有预设关系的个体n,我们可以重新定义网络中个体之间的关系,从而实现关系的转换。这种方法在处理复杂的社交网络关系时非常有用,因为它允许我们通过简单的合并操作来解决更复杂的关系问题。
是敌人的话,问什么不能这么写:
p[find(b)] = find(a+n);
p[find(a)] = find(b+n);
他把敌人x赋值到了p[x+n],所以有朋友1~n和敌人n+1~2n,所以要开到2n
i<=2*n 这是为什么?求大神赐教
这里,n 是强盗的数量,2*n+1 是数组 p 的大小,它足够容纳所有强盗以及他们的“影子”(shadow)强盗。每个强盗 i 都有一个对应的“影子”强盗 i+n。这样做的目的是为了区分朋友关系和敌人关系,因为并查集只能处理合并操作,不能直接处理“不是朋友”的关系。
当 op == ‘F’ 时,表示 a 和 b 是朋友,我们直接合并 a 和 b 的根节点。
当 op == ‘E’ 时,表示 a 和 b 是敌人。我们不能直接合并 a 和 b,因为这样会违反题目中的规则。相反,我们合并 a 的“影子”和 b,以及 b 的“影子”和 a。这样,即使 a 和 b 在同一个团伙中,他们的“影子”也会在不同的团伙中,从而确保 a 和 b 不会直接成为朋友。
通过这种方式,我们可以确保:
如果 a 和 b 是朋友,他们将被合并到同一个团伙中。
如果 a 和 b 是敌人,他们将通过他们的“影子”间接地成为朋友,但不会直接合并,从而保持敌人关系。
最后,我们遍历 p 数组,统计根节点的数量,这代表了最大可能的团伙数。每个根节点代表一个独立的团伙,因为它们没有被合并到其他团伙中。这就是为什么 res 变量用于计数根节点,即团伙的数量。
为什么给p赋值到2*n
容纳所有强盗以及他们的“影子”(shadow)强盗,我们合并 a 的“影子”和 b,以及 b 的“影子”和 a。这样,即使 a 和 b 在同一个团伙中,他们的“影子”也会在不同的团伙中,从而确保 a 和 b 不会直接成为朋友。因为a和b通过影子分开到2个集合了