扩展域并查集:基于枚举的思想,把每个点拆开,分到若干个域里(一般是两个,自己和对立),对于每个条件,如果发生了矛盾,就可以更新或输出答案,若为产生矛盾,则将相对应的域合并。
(一般来说对于点a, a表示本体所在的域,a+n表示其相对域)
带权并查集:相对于普通并查集来说,需要额外维护一个信息:自己与根节点的相对关系。核心是我们知道了其与根节点的相对关系,也就知道了各点之间的关系。
核心代码:
int find(int x)
{
if(x!=p[x]){
int t=p[x];
p[x]=find(p[x]);
d[x]+=d[t];
}
return p[x];
}
对于一些问题,我们可以同时用两种方法来解决,但是需要注意,扩展域并查集在关系较多时占用空间较大,此时不可取,必须用带权并查集来解决。
例题:239. 奇偶游戏 - AcWing题库
扩展域解法:
#includeusing namespace std; const int N=4e4+10; int p[N],d[N]; unordered_map mp; int cnt; int get(int x) { if(!mp.count(x)) mp[x]=++cnt; return mp[x]; } int find(int x) { if(x!=p[x]){ p[x]=find(p[x]); } return p[x]; } signed main() { int n,m; cin>>n>>m; int ans=m; for(int i=1;i >a>>b>>ss; a=get(a-1),b=get(b);//离散化 if(ss=="odd"){//如果为异类 if(find(a)==find(b)){//而如果此时a,b却在同一个集合内 ans=i-1; break; } p[find(a+n)]=find(b);//a,b奇偶性不同,将其对应的域合并 p[find(a)]=find(b+n); } else { if(find(a+n)==find(b)){ ans=i-1; break; } p[find(a)]=find(b);//a,b奇偶性相同,将其对应的域合并 p[find(a+n)]=find(b+n); } } cout<
带权并查集解法:
#includeusing namespace std; const int N=2e4+10; int p[N],d[N]; unordered_map mp; int cnt; int get(int x) { if(!mp.count(x)) mp[x]=++cnt; return mp[x]; } int find(int x) { if(x!=p[x]){ int t=p[x]; p[x]=find(p[x]); d[x]+=d[t]; } return p[x]; } signed main() { int n,m; cin>>n>>m; int ans=m; for(int i=1;i >a>>b>>ss; int t=0;//默认是同类 if(ss=="odd") t=1; a=get(a-1),b=get(b);//离散化 int pa=find(a),pb=find(b); if(pa==pb){//如果在同一个集合内,判断是否正确 if(((d[a]+d[b])%2+2)%2 != t) {//如果不正确 ans=i-1; break; } } else {//不再一个集合内,就更新 p[pa]=pb; d[pa]=d[b]-d[a]-t;//d[b]=d[a]+d[pa]+t,这里+和-一样,因为在模2情况下 } } cout<
240. 食物链 - AcWing题库
带权并查集解法:
#includeusing namespace std; const int N=5e4+10; int p[N],d[N]; int n,k; int find(int x) { if(x!=p[x]){ int t=p[x]; p[x]=find(p[x]); d[x]=d[x]+d[t]; } return p[x]; } signed main() { cin>>n>>k; int ans=0; for(int i=1;i<=n;i++) p[i]=i; while(k--) { int v,a,b; cin>>v>>a>>b; if(a>n||b>n) { ans++; continue; } int pa=find(a),pb=find(b); if(v==1){ if(pa==pb){//在同一个集合内 if((d[a]-d[b])%3){//如果不是同类关系 ans++; } } else {//不在一个集合内,要合并 p[pa]=pb; d[pa]=d[b]-d[a];//更新信息,可以画图理解 //因为要满足d[b]=d[pa]+d[a] } } else{ if(a==b) {ans++; continue;} else { if(pa==pb){ if((d[a] - d[b] - 1) % 3) ans++; } else { p[pa]=pb;//要满足d[b]=d[pa]+d[a]-1; d[pa]=d[b]-d[a]+1; } } } } cout<
本题也可用扩展域来求解,不过略微复杂。



