int fa[N]; inlinevoidinit(int n){ for (int i = 1; i <= n; ++i) fa[i] = i; } intfind(int x){ return x == fa[x] ? x : (fa[x] = find(fa[x])); } inlinevoiduno(int x, int y){ fa[find(x)] = find(y); }
按秩合併:
1 2 3 4 5 6 7 8 9
int fa[N], rnk[N]; inlinevoidinit(int n){ for (int i = 1; i <= n; ++i) fa[i] = i, rnk[i] = 1; } intfind(int x){ return x == fa[x] ? x : (fa[x] = find(fa[x])); } inlinevoiduno(int i, int j){ int x = find(i), y = find(j); if (rnk[x] <= rnk[y]) fa[x] = y; else fa[y] = x; if (rnk[x] == rnk[y] && x != y) rnk[y]++; }
帶權併查集:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
int fa[N], num[N], front[N]; inlinevoidinit(int n){ for (int i = 1; i <= n; ++i) fa[i] = i, num[i] = 1, front[i] = 0; } intfind(int x){ if (fa[x] != x) { int fa_x = fa[x]; fa[x] = find(fa[x]); front[x] += front[fa_x]; // 此依題而異 } return fa[x]; } inlinevoiduno(int x, int y){ int fa_x = find(x), fa_y = find(y); front[fa_x] += num[fa_y], num[fa_y] += num[fa_x], num[fa_x] = 0; // 將x移動至y隊列的後面 此依題而異 fa[fa_x] = fa_y; }
一些常用:
求元素k 所在連通圖的元素數量Sk={1⩽i⩽n∣find(k)=find(i)}:
1
for (int i = 1; i <= n; ++i) Sk += find(k) == find(i);
inlinevoiduno(int x, int y, bool opt){ int fa_x = find(x), fa_y = find(y); if (fa_x != fa_y) { tf[fa_x] = tf[x] ^ tf[y] ^ !opt; // 此依題而異 fa[fa_x] = fa_y; } }
voideachT(){ int n; init(n);
bool flag = 1; for (int x = 1; x <= n; ++x) { int y = read(), opt = read(); // x 說 y 是 opt 的 if (find(x) == find(y) && (tf[x] ^ tf[y] ^ !opt) == 1) flag = 0; // 父級相同 但關係矛盾 elseuno(x, y, opt); }
if (!flag) returnputs("No answer"), void();
for (int i = 1; i <= n; ++i) cnt[find(i)][tf[i]] += 1; // 記錄連通圖中0和1的个數
int ans = 1, cntmx = 0, cntmn = 0; for (int i = 1; i <= n; ++i) { if (find(i) == i) { // 根節点 ans <<= 1; cntmx += max(cnt[i][1], cnt[i][0]); cntmn += min(cnt[i][1], cnt[i][0]); } } print(ans, cntmx, cntmn); }