题意 克苏鲁:An undirected graph that can be represented as a set of three or more rooted trees, whose roots are connected by a simple cycle. (VJspr1F - Cthulhu)
思路 是连通图,且仅包含一个环。
如果在合并连通图时,发现两个元素的根节点相同,那么必然形成一个环
树满足v=e+1,因此仅包含一个环时有v=e。
1 2 3 4 5 6 7 8
int n = read(), e = read(); DSU dsu(n); int cnt = 0; for (int i = 0; i < e; ++i) { int x = read(), y = read(); if(!dsu.uno(x, y)) cnt += 1; // 父级相同 有环 } printf("%s\n", cnt == 1 && n == e ? "FHTAGN!" : "NO"); // WAon46
RMQ rmq(res); ll lstans = 0; while (q--) { int a = read(), b = read(), x = read(), y = read(); int l = (a * lstans + x - 1) % n + 1; int r = (b * lstans + y - 1) % n + 1; if (l > r) swap(l, r); node cur = res[rmq.getmax(l, r)]; printf("%lld %d\n%d %d %lld\n", cur.x, cur.y, l, r, lstans); lstans = cur.x * cur.y % n; } }
int n = read(), m = read(); DSU dsu(n); for (int i = 0; i < m; ++i) { int u = read(), v = read(); dsu.uno(u, v); } int res = 0; for (int i = 1; i <= n; ++i) { res += dsu.find(i) == i; } printf("%d\n", m - n + res);
思路二
1 2 3 4 5 6 7 8
int n = read(), m = read(); DSU dsu(n); int res = 0; for (int i = 0; i < m; ++i) { int u = read(), v = read(); if (!dsu.uno(u, v)) res += 1; } printf("%d\n", res);
constexprint D = 30; int n = read(), m = read(); vector<DSU> dsu(D + 1, DSU(n)); while (m--) { int l1 = read(), r1 = read(), l2 = read(), r2 = read(); int len = r1 - l1 + 1; // 区间长度
for (int d = D; d >= 0; d--) { if (len & (1ll << d)) { dsu[d].uno(l1, l2); // 把对应区间并起来 l1 += (1ll << d); l2 += (1ll << d); // 倍增跳 } } } for (int d = D - 1; d >= 0; d--) { for (int i = 1; i + (1ll << d) <= n; ++i) { int p = dsu[d + 1].find(i); if (i == p) continue;
// 前一半和后一半的并查集合并 dsu[d].uno(i, p); dsu[d].uno(i + (1ll << d), p + (1ll << d)); } } int siz = 0; for (int i = 1; i <= n; ++i) { siz += dsu[0].find(i) == i; } printf("%lld\n", (9 * qpow(10, siz - 1)) % mod);
intfind(int x){ if (x != p[x]) { int t = p[x]; p[x] = find(p[x]); dep[x] += dep[t]; } return p[x]; }
boolsame(int x, int y, int d){ returnfind(x) == find(y) && (dep[x] + d - dep[y]); }
booluno(int x, int y, int d){ // 把y合并到x上 希望dep[x]+d=newdep[y]=dep[py]+dep[y] if (same(x, y, d)) returnfalse; int dx = dep[x], dy = dep[y]; x = find(x), y = find(y); dep[y] = dx + d - dy; p[y] = x; siz[x] += siz[y]; // 合并到x returntrue; }
intfind(int x){ if (x != p[x]) { int t = p[x]; p[x] = find(p[x]); dep[x] += dep[t]; } return p[x]; }
boolsame(int x, int y, int d){ returnfind(x) == find(y) && (dep[x] + d - dep[y]); }
booluno(int x, int y, int d){ // 把y合并到x上 希望dep[x]+d=newdep[y]=dep[y]+dep[y] if (same(x, y, d)) returnfalse; int dx = dep[x], dy = dep[y]; x = find(x), y = find(y); dep[y] = dx + d - dy; p[y] = x; siz[x] += siz[y]; // 合并到x mxdep[x] = max(mxdep[x], mxdep[y] + dep[y]); returntrue; }
intsize(int x){ return siz[find(x)]; } };
voideachT(){ int n = read();
SSN dsu(n);
for (int i = 2; i <= n; ++i) { int p = read(), son = read(), q = read(); dsu.uno(p, son, 1); printf("%lld ", dsu.mxdep[q]); } printf("\n"); }
LRP(int n, int m) : mod(m), p(n + 1), siz(n + 1, 1), dep(n + 1) { iota(p.begin(), p.end(), 0); }
intfind(int x){ if (x != p[x]) { int t = p[x]; p[x] = find(p[x]); dep[x] = (dep[x] + dep[t]) % mod; } return p[x]; }
boolsame(int x, int y, int d){ returnfind(x) == find(y) && (dep[x] + d - dep[y]) % mod; }
booluno(int x, int y, int d){ // 把y合并到x上 希望dep[x]+d=newdep[y]=dep[py]+dep[y] if (same(x, y, d)) returnfalse; int dx = dep[x], dy = dep[y]; x = find(x), y = find(y); dep[y] = dx + d - dy; dep[y] = (dep[y] % mod + mod) % mod; p[y] = x; siz[x] += siz[y]; // 合并到x returntrue; }
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; } }
统计 首先是判断是否有解:
1 2 3 4 5 6 7
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) { puts("No answer"); return; }
typedeflonglong ll; constexpr ll maxN = 1e6 + 7; constexpr ll MOD = 998244353; inline ll max(ll a, ll b){ return a > b ? a : b; } inline ll min(ll a, ll b){ return a < b ? a : b; }
int fa[maxN]; bool tf[maxN]; // 和根節點的關系 1or0 int cnt[maxN][2]; bool flag = 1;
//int fa[maxN], rank[maxN]; inlinevoidinit(int n){ for (int i = 1; i <= n; ++i) fa[i] = i; } /// 和普通的并查集的init()一模一樣
int n = read(), q = read(); LRP dsu(n, 3); int res = 0; while (q--) { int d = read() - 1, x = read(), y = read(); res += (x > n || y > n || !dsu.uno(x, y, d)); } printf("%d\n", res);
int n = read(), q = read(); LRP dsu(n + 1); int res = 0; while (q--) { int l = read(), r = read() + 1, d = read(); res += (!dsu.uno(l, r, d)); } printf("%d\n", res);
int n = read(), q = read(); DSU dsu(2 * n); while (q--) { int x = read(), y = read(); dsu.uno(x, y + n); dsu.uno(x + n, y); } bool ok = 1; for (int i = 1; i <= n; ++i) { if (dsu.same(i, n + i)) ok = 0; } printf("%s\n\n", ok ? "No suspicious bugs found!" : "Suspicious bugs found!"); // PE
评注 我的方法是依“选择题”改编,前面的部分从略,主函数:
1 2 3 4 5 6 7 8
int n = read(), q = read(); DSU dsu(n, 2); bool ok = 1; while (q--) { int x = read(), y = read(); if (!dsu.uno(x, y, 1)) ok = 0; // 父级相同 但关系矛盾 } printf("%s\n\n", ok ? "No suspicious bugs found!" : "Suspicious bugs found!"); // PE
int n = read(), q = read(); DSU dsu(3 * n); int res = 0; while (q--) { int opt = read(), x = read(), y = read(); if (x > n || y > n) { res += 1; } elseif (opt == 1) { // 同类 if (dsu.same(x + n, y) || dsu.same(x, y + n)) { res += 1; } else { dsu.uno(x, y); dsu.uno(n + x, n + y); dsu.uno(n + n + x, n + n + y); } } else { if (dsu.same(x, y) || dsu.same(x + n, y)) { res += 1; } else { dsu.uno(x, n + y); dsu.uno(n + x, n + n + y); dsu.uno(n + n + x, y); } } } printf("%d\n", res);