BZOJ 1478 & 1488 & 1815 & Sgu282 Isomorphism
边染色,顶点的置换导致同构,暴力枚举置换有 $n!$ 种,考虑有多少种置换的贡献相同。
点的一个置换中,对顶点 $u$ 有 $f: u \to p[u]$,边 $(u, v)$ 的置换 $f':(u,v) \to (p[u], p[v])$。
当 $u, v$ 同时在一个循环中,循环长度为 $L$,那么这样的边 $(u,v)$ 循环个数为 $\lfloor \frac{L}{2} \rfloor$。
当 $u, v$ 分别在长度为 $L_1$,$L_2$ 的循环中时,边 $(u,v)$ 形成的循环长度为 $\text{lcm}(L_1, L_2)$,循环个数为 $\gcd(L_1, L_2)$。
循环个数只跟 $L$ 有关,那么看 $L$ 可以怎么构成。
现在就是把 $n$ 个顶点分成若干个循环。设有 $m$ 个循环,循环长度分别为 $L_1, L_2, ..., L_m$,其中 $0 < L_1 \leq L_2 \leq ... \leq L_m$,$L_1 + L_2 + ... + L_m = n$。
一种分法确定后,求一下有多少种置换。
相当于把 $n$ 个元素放进 $m$ 个盒子,每个盒子大小为 $L_i$,方案数为 $\frac{n!}{L_1!L_2!...L_m!}$,每个盒子里的有 $(L_i - 1)!$ 种排列方式,那么方案数为 $\frac{n!}{L_1L_2...L_m}$,若有 $L_i = L_{i+1} = ... =L_{i + k - 1}$,这 $k$ 个盒子是无区别的,那么就得再除以 $k!$。
所以 $L_1...L_m$ 确定后,就有 $$\frac{n!}{L_1L_2...L_mk_1!k_2!...k_t!}$$
其中 $t$ 表示 $L$ 有 $t$ 种取值,每种取值出现了 $k_i$ 次。
对于 BZOJ1488 这道图同构的,相当于给完全图的边染上两种颜色,白色表示该边存在,黑色表示该边不存在,就是 $m=2$ 的情况。
#include <bits/stdc++.h> #define ll long long const int N = 100; int n, m, MOD; int fac[N], Ans, tol; int num[N], cnt[N]; int gcd(int a, int b) { while (b) { a %= b; std::swap(a, b); } return a; } int qp(int a, int b = MOD - 2) { int ans = 1; while (b) { if (b & 1) ans = 1LL * a * ans % MOD; a = 1LL * a * a % MOD; b >>= 1; } return ans; } void Ma(int &a) { if (a >= MOD) a -= MOD; if (a < 0) a += MOD; } template<class T> void Mb(T &a) { static const int mod = MOD - 1; a %= mod; if (a >= mod) a -= mod; if (a < 0) a += mod; } void dfs(int cur, int left) { if (!left) { int a = 1; ll b = 0; for (int i = 1; i <= tol; i++) { a = 1LL * a * qp(num[i], cnt[i]) % MOD * fac[cnt[i]] % MOD; b += cnt[i] * (cnt[i] - 1) / 2 * num[i] + num[i] / 2 * cnt[i]; for (int j = i + 1; j <= tol; j++) b += cnt[i] * cnt[j] * gcd(num[i], num[j]); } a = 1LL * fac[n] * qp(a) % MOD; Mb(b); Ma(Ans += 1LL * a * qp(m, (int)b) % MOD); } if (cur > left) return; dfs(cur + 1, left); for (int i = 1; i * cur <= left; i++) { ++tol; num[tol] = cur; cnt[tol] = i; dfs(cur + 1, left - i * cur); --tol; } } int main() { scanf("%d%d%d", &n, &m, &MOD); for (int i = fac[0] = 1; i < N; i++) fac[i] = 1ll * i * fac[i - 1] % MOD; dfs(1, n); printf("%lld\n", 1LL * Ans * qp(fac[n]) % MOD); return 0; }