並查集(擴充套件)
阿新 • • 發佈:2021-07-02
前言
本文作為 並查集 的補充說明和擴充套件。
正文
我們在做並查集時,\(f_i\) 一般表達的是親戚關係,但在這道題 P1892 中有兩種關係,一種為親戚關係,另一種為敵對關係,這就涉及到了另一個知識:反集。\(i\) 的反集一般用 \(f_{i+n}\) 來表示,其他的基本與並查集無異(但是在join(i+n,j)
中,兩個引數千萬不能寫反),上程式碼(P1892):
#include <iostream> using namespace std; int n,m,ans; char c; int f[2001]; int find(int x) { if (f[x]==x) { return x; } return f[x]=find(f[x]); } void join(int x,int y) { int nx=find(x),ny=find(y); if (nx!=ny) { f[nx]=ny; } } int main() { #ifndef ONLINE_JUDGE freopen("1.in","r",stdin); freopen("1.out","w",stdout); #endif cin>>n>>m; for (int i=1;i<=2*n;i++) { f[i]=i; } for (int i=1;i<=m;i++) { cin>>c; int t1,t2; cin>>t1>>t2; if (c=='F') { join(t1,t2); } else if (c=='E') { join(t1+n,t2); join(t2+n,t1); } } for (int i=1;i<=n;i++) { if (f[i]==i) { ans++; } } cout<<ans; }
可以發現基本與並查集無異,只是需要多寫一個join
。
如有不懂還可參考 there。
再來看一道題 P2024 ,第一眼看上去就覺得是一個反集,於是便自信的打了一個反集上去,然後,成功地連樣例都沒過。
………
這是為啥,仔細讀題你會發現,題目中的關係為:\(A\) 吃 \(B\),\(B\) 吃 \(C\),\(C\) 吃 \(A\),已就是說 \(A\) 吃 \(B\),\(B\) 不吃 \(A\),所以反集不成立(因為這是單向關係),所以我們就素要把 \(f\) 陣列再開一倍(也就是開三倍)去儲存關係,即 \(f_i\) 表示為 \(i\) 的親戚,\(f_{i+n}\) 表示為 \(i\)
#include <iostream> #include <cstdio> using namespace std; int f[300005]; int n,k,ans; inline int read() {//要寫快讀,cin過不去 int sum=0; char ch=getchar(); while(ch>'9'||ch<'0') ch=getchar(); while(ch>='0'&&ch<='9') sum=sum*10+ch-48,ch=getchar(); return sum; } int find(int x) { if (x==f[x]) { return x; } return f[x]=find(f[x]); } void join(int x,int y) { int nx=find(x),ny=find(y); f[nx]=ny; } int main() { #ifndef ONLINE_JUDGE freopen("1.in","r",stdin); freopen("1.out","w",stdout); #endif int x,y,z; n=read(),k=read(); // cin>>n>>k; for (int i=1;i<=3*n;++i) { f[i]=i; } for(int i=1;i<=k;++i) { z=read(),x=read(),y=read(); // cin>>z>>x>>y; if (x>n or y>n) { ans++; continue; } if(z==1) { if(find(x+n)==find(y) or find(x+n+n)==find(y)) { ans++; } else { join(x,y); join(x+n,y+n); join(x+n+n,y+n+n); } } else if (z==2) { if (x==y) { ans++; } else if (find(x)==find(y) or find(x+2*n)==find(y)) { ans++; } else { join(x,y+n+n); join(x+n,y); join(x+n+n,y+n); } } } printf("%d\n",ans); return 0; }
並查集的擴充套件應用實際就是開多倍的 \(f\) 陣列去表達關係,並沒有什麼難處。
\(finally\)
最後,以上操作均可使用帶權並查集實現(只不過我不會,逃)。