题解 P4727 【[HNOI2009]图的同构记数】
一道Burnside题的流程大概是两步:
- 找出置换群
<G,*> 和目标集合S 。 - 求出
G 中每一个置换在S 中的不动点集合大小之和。
记一个置换
此题中,置换群
显然
所以,不妨把
实际上,每一个
现在我们通过dfs得到了一个单调不下降的序列
前一个值是:
其中
这个的证明可以用传统的排列组合知识解决。
后一个值较为复杂。后一个值要求的就是有多少种连边的方法,使得在图的点编号被置换一次之后,图的各个循环内部以及循环之间仍然是一模一样的。
我们需要再次把问题分成两部分:循环内和循环间。
对于
到这里,需要发现的一点是,因为边是无向的,所以如果k连出的边中,有某一条边跨度超过了
那么,每一个循环内部的方案数就是
(这也就是yyb题解中说的“一个n个点的循环只有
现在考虑循环之间的连边方案数。从
如果
综上所述,对于一个划分
再把这个值乘上
就是这个划分
把所有划分的答案加起来之后,根据Burnside定理的公式,还要乘上一个
而这个
把所有
/*
几个错误调试了好久。
*///using CRLF, UTF-8
#include <cstdio>
#include <algorithm>
#include <cstring>
#define pr printf
#define F(i, j, k) for(register int i = j, kkllkl = k; i <= kkllkl; ++i)
#define G(i, j, k) for(register int i = j, kkllkl = k; i >= kkllkl; --i)
#define clr(a, v) memset((a), v, sizeof(a))
using namespace std;
typedef long long ll;
#define isd(x) (('0' <= (x) && (x) <= '9') || (x) == '-')
int rd() {
int ans = 0, sign = 1; char c = getchar();
while(!isd(c)) c = getchar();
if(c == '-') sign = -1, c = getchar();
while(isd(c)) ans = (ans << 3) + (ans << 1) + c - '0', c = getchar();
return sign == 1 ? ans : -ans;
}
#define chkmin(x, y) ((x) = min((x), (y)))
#define chkmax(x, y) ((x) = max((x), (y)))
/* --------------------------------------------------- */
const int N = 65, MOD = 997;
int Gcd(int x, int y) { return y ? Gcd(y, x % y) : x; }
int fsp(int a, int x) {
int ans = 1;
for(; x; x >>= 1, a = a * a % MOD)
if(x & 1) ans = ans * a % MOD;
return ans;
}
int n, frac[N], ifrac[N], gcd[N][N], p2[N], inv[N];
int a[N], m, b[N];
int solve() {
// F(i, 1, m) pr("%d%c", a[i], " \n"[i == m]);
int ret = 1;
F(i, 1, m) ret = (ret * inv[a[i]]) % MOD;
F(i, 1, n) ret = (ret * ifrac[b[i]]) % MOD;
F(i, 1, m) ret = (ret * p2[a[i] / 2]) % MOD;
F(i, 1, m) F(j, i + 1, m) ret = (ret * p2[gcd[a[i]][a[j]]]) % MOD;
// pr("ret = %d\n", ret);
return ret;
}
int ans = 0;
void dfs(int level, int sum) {
if(sum == n) {
m = level - 1;
ans = (ans + solve()) % MOD;
return ;
}
F(i, max(1, a[level - 1]), n - sum) {
a[level] = i;
++b[i];
dfs(level + 1, sum + i);
--b[i];
}
}
int main() {
n = rd();
F(i, 1, n) inv[i] = fsp(i, MOD - 2);
frac[0] = 1;
F(i, 1, n) frac[i] = frac[i - 1] * i % MOD;
ifrac[n] = fsp(frac[n], MOD - 2);
G(i, n - 1, 0) ifrac[i] = ifrac[i + 1] * (i + 1) % MOD;
F(i, 1, n) F(j, 1, n) gcd[i][j] = Gcd(i, j);
p2[0] = 1;
F(i, 1, n) p2[i] = (p2[i - 1] * 2) % MOD;
dfs(1, 0);
pr("%d\n", ans);
return 0;
}
/*
*/