您的位置:首页 > 其它

【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的拆分个数。

【代码】

#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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: