【BZOJ1488】【HNOI2009】图的同构
2018-01-11 19:56
399 查看
【题目链接】
点击打开链接
【思路要点】
考虑Burnside引理或Polya定理,求解总共\(N!\)种点置换中边的染色方案等价类的个数总和,再将答案除以N!。
考虑一个点集的置换\(P_{i}\),若将一个置换看做一张由\(N\)个点、\(N\)条边的无向图,那么显然,该图由若干个环组成。记其中每一个环长为\(A_{i}\),\(A\)可以看做一个\(N\)的拆分。
60的拆分数为966467,枚举是可以接受的。
不难发现,一个置换中边的染色方案等价类的个数只与上述拆分\(A\)有关。
枚举N的整数拆分,分别计算拆分出现的次数\(tmp\)与置换中边的染色方案等价类的个数\(now\),将答案\(Ans\)加上\(tmp*2^{now}\)。
考虑由点集的置换推边集的置换,\(now\)应当为\(A_{i}\)两两的\(Gcd\)之与\([\frac{A_{i}}{2}]\)的和。
\(tmp\)可以运用一定的组合数学的计算得出。
时间复杂度\(O(∑^{N}_{i=1}i^{2}*S(N,i))\)其中\(S(N,i)\)表示整数N长度为i的拆分个数。
【代码】
点击打开链接
【思路要点】
考虑Burnside引理或Polya定理,求解总共\(N!\)种点置换中边的染色方案等价类的个数总和,再将答案除以N!。
考虑一个点集的置换\(P_{i}\),若将一个置换看做一张由\(N\)个点、\(N\)条边的无向图,那么显然,该图由若干个环组成。记其中每一个环长为\(A_{i}\),\(A\)可以看做一个\(N\)的拆分。
60的拆分数为966467,枚举是可以接受的。
不难发现,一个置换中边的染色方案等价类的个数只与上述拆分\(A\)有关。
枚举N的整数拆分,分别计算拆分出现的次数\(tmp\)与置换中边的染色方案等价类的个数\(now\),将答案\(Ans\)加上\(tmp*2^{now}\)。
考虑由点集的置换推边集的置换,\(now\)应当为\(A_{i}\)两两的\(Gcd\)之与\([\frac{A_{i}}{2}]\)的和。
\(tmp\)可以运用一定的组合数学的计算得出。
时间复杂度\(O(∑^{N}_{i=1}i^{2}*S(N,i))\)其中\(S(N,i)\)表示整数N长度为i的拆分个数。
【代码】
#include<bits/stdc++.h> using namespace std; const int P = 997; const int MAXN = 65; template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int a[MAXN], b[MAXN][MAXN]; int fac[MAXN], inv[MAXN], n, ans, now; int power(int a, int b) { if (b == 0) return 1; int tmp = power(a, b / 2); if (b % 2 == 0) return tmp * tmp % P; else return tmp * tmp % P * a % P; } int gcd(int a, int b) { if (b == 0) return a; else return gcd(b, a % b); } int getc(int x, int y) { return fac[x] * inv[y] % P * inv[x - y] % P; } void check(int depth) { a[depth + 1] = now = 0; for (int i = 1; i <= depth; i++) { now += a[i] / 2; for (int j = i + 1; j <= depth; j++) now += b[a[i]][a[j]]; } int lft = n, last = 0, tmp = 1; for (int i = 1; i <= depth; i++) { tmp = tmp * fac[a[i] - 1] % P; tmp = tmp * getc(lft, a[i]) % P; lft -= a[i]; if (a[i] != a[i + 1]) { tmp = tmp * inv[i - last] % P; last = i; } } ans = (ans + tmp * power(2, now) % P) % P; } void work(int depth, int sum, int now) { if (sum == 0) { check(depth - 1); return; } if (now == 0) return; work(depth, sum, now - 1); if (sum >= now) { a[depth] = now; work(depth + 1, sum - now, now); } } int main() { read(n); for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) b[i][j] = gcd(i, j); fac[0] = inv[0] = 1; for (int i = 1; i <= n; i++) { fac[i] = fac[i - 1] * i % P; inv[i] = power(fac[i], P - 2); } work(1, n, n); ans = ans * inv % P; printf("%d\n", ans); return 0; }
相关文章推荐
- bzoj1488 [HNOI2009]图的同构(群论+组合数学+polya)
- bzoj 1488: [HNOI2009]图的同构
- [bzoj1488][HNOI2009]图的同构——Polya定理
- bzoj1488[HNOI2009] 图的同构
- BZOJ 1488: [HNOI2009]图的同构
- [bzoj1488][HNOI2009]图的同构——Polya定理
- [BZOJ1488][HNOI2009]图的同构(Polya定理+组合数)
- bzoj 1488: [HNOI2009]图的同构 (置换+dfs)
- 【HNOI2009】bzoj1488 图的同构
- [bzoj1488][hnoi2009]同构图
- BZOJ 1488: [HNOI2009]图的同构 [Polya]
- BZOJ 1488 [HNOI2009]图的同构 Polya定理
- [BZOJ1488][HNOI2009]图的同构(dfs+置换群+数论+组合数学)
- BZOJ1488 [HNOI2009]图的同构
- BZOJ 1488: [HNOI2009]图的同构 polay
- bzoj 1483: [HNOI2009]梦幻布丁 (set)
- 【bzoj1483】 HNOI2009—梦幻布丁
- bzoj1485: [HNOI2009]有趣的数列
- bzoj 1485: [HNOI2009]有趣的数列 卡特兰数
- BZOJ1486: [HNOI2009]最小圈