emmm又是一个很高大上的概念
很久前就接触过,可惜直到现在还是摸棱两可。
核心概念就是:朋友的朋友是我的朋友
敌人的敌人是我的敌人
常见的做法是将原并查集扩大一倍规模,并划分为两个种类。
在同个种类的并查集中合并,和原始的并查集没什么区别,仍然表达他们是朋友
这个含义。
考虑在不同种类的并查集中合并的意义,其实就表达 他们是敌人
这个含义了。
按照并查集美妙的 传递性
,我们就能具体知道某两个元素到底是 敌人
还是 朋友
了。
如果a和b是敌人,合并n+b和a,n+a和b
如果c和a是敌人,合并n+c和a,n+a和c
那么b和c就并在一起了
因为a成为了他们的纽带
那么,我们来看一下这道题目。关押犯人https://www.luogu.com.cn/problem/P1525
题面:n个犯人,m对关系,表示犯人A和犯人B的冲突值。
现在要把犯人分成两类,要求在同一个监狱中犯人的最大冲突值最小。
可以注意到,要减小冲突值,首先我们不能把冲突最大的分到一起。
比如,我们知道1和2肯定在不同的监狱,因为他们的冲突值太大了。
然后我们再考虑,在这个基础上,4和3也最好也在不同的监狱
同理,1和3也是这样,2和3,以此类推
但是我们不能无限这样做下去,因为我们不能保证都在不同的监狱。
我们这么想:1和2是敌人
4和3也是敌人
1和3是敌人
2和3是敌人
是不是发现,敌人的敌人就是我的朋友.
然后我们枚举到2和3的时候,发现他们是朋友
不可避免地,2和3冲突了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; int pre[40009],n,m,l,r; struct p{ int l,r,v; bool operator < (const p&tmp) const{ return v>tmp.v; } }a[100009]; int find(int x){ if(x!=pre[x]) pre[x]=find(pre[x]); return pre[x]; } void join(int q,int w){ pre[find(q)]=find(w); return; } int main() { cin>>n>>m; for(int i=1;i<=m;i++) cin>>a[i].l>>a[i].r>>a[i].v; sort(a+1,a+1+m); for(int i=1;i<=n+n;i++) pre[i]=i; for(int i=1;i<=m;i++) { int l=a[i].l,r=a[i].r; if(find(l)==find(r)) { cout<<a[i].v; return 0; } join(l+n,r); join(r+n,l); } cout<<0; }