-
题意:
- 求(n)个点,(m)条边的同构仙人球个数.
- (nle 1000)
-
这是一道怎么看怎么不可做的题。
-
这种题,肯定是圆方树啦~
-
好,那么首先转为广义圆方树.
-
圆方树上有两种点(废话),那么对于一个方点,它实际上代表的是一个点双,所以我们需要判断一个方点的子树是否中间对称,如果对称则这个子树答案乘(2).
-
显然.
-
然后判断一个圆点与几个方点相连时,注意到方点之间是可以互相交换顺序的,于是我们看看有多少个子树相同,乘个阶乘.
-
最后就是求同构仙人球的个数了.
-
这个比较麻烦,但也不难。。
-
实质上我们可以求一个(Hash)值,每次从一个点的父亲节点对应的那条边开始找,找到最后一条边,然后再从第一条边开始找,找到父亲那条边.
-
这样,对于单独一个环,在环内的两个点,父亲的那条边始终是相同的,所以我们就正反扫一遍找到的边,然后取个Hash值的(min)
-
实现比较困难,但需要奋斗!
-
这里有个小trick,那就是当我们缩点双时,一定要按照一个固定的顺序去缩,具体代码里会有说。。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define I register int
#define F(i, a, b) for (I i = a; i <= b; i ++)
#define G(i, a, b) for (I i = a; i >= b; i --)
#define mem(a, b) memset(a, b, sizeof a)
#define mec(a, b) memcpy(a, b, sizeof a)
#define mn(a, b) ((a) = (a) < (b) ? (a) : (b))
#define mx(a, b) ((a) = (a) > (b) ? (a) : (b))
#define Get getchar()
const int N = 2010, M = 30 * N, Z[5] = {1532531, 12136371, 1232535, 1846315, 1463322}, Mo[5] = {16232312, 12322323, 12552344, 74556263, 13223623}, mo = 1000000003LL;
using namespace std;
int n, m, x, y, answer, bz, New, cnt, top, rt, len, dfn[M], low[M], d[M], size[N], Ans[N][5], HASH[N][5], hash[N][5], ans[M], H[M], jc[M], fath[M], vis[M];
int tov[M], nex[M], las[M], tot;
int To[M], Nx[M], Ls[M], TOT;
struct node { int v , num; } D[M];
void R(I &x) {
char c = Get; x = 0; I t = 1;
for (; !isdigit(c); c = Get) t = (c == '-' ? -1 : t);
for (; isdigit(c); x = (x << 3) + (x << 1) + c - '0', c = Get); x *= t;
}
bool cmp(node x, node y) { return x.v < y.v; }
void ins(I x, I y) { tov[ ++ tot ] = y, nex[ tot ] = las[x], las[x] = tot; }
void link(I x, I y) {
To[++ TOT] = y, Nx[TOT] = Ls[x], Ls[x] = TOT;
To[++ TOT] = x, Nx[TOT] = Ls[y], Ls[y] = TOT;
}
void Tarjan(I k, I edge) {
dfn[k] = low[k] = ++ cnt, d[++ top] = k;
for (I x = las[k]; x; x = nex[x])
if (!dfn[tov[x]]) {
Tarjan(tov[x], x), mn(low[k], low[tov[x]]);
if (low[tov[x]] >= dfn[k]) {
link(k, ++ New), link(d[top --], New);
while (d[top + 1] ^ tov[x]) link(d[top --], New);
//我最后两句话想说的就是这个地方,千万不能打成这样:
//link(d[top --], ++ New), link(k, New);
//while (d[top + 1] ^ tov[x]) link(d[top --], New);
//为什么不必多说,意会意会
}
} else if (x ^ (edge ^ 1)) mn(low[k], dfn[tov[x]]);
}
void GetHash(I k, I fa) {
size[k] ++;
for (I x = Ls[k] ; x ; x = Nx[x])
if (To[x] ^ fa) GetHash(To[x], k), size[k] += size[To[x]];
for (len = 0, x = Ls[k]; x ; x = Nx[x])
if (To[x] ^ fa) D[++ len] = {size[To[x]], To[x]};
sort(D + 1, D + len + 1, cmp);
F(i, 0, 4) {
HASH[k][i] = 1;
F(j, 1, len) HASH[k][i] = (1LL * HASH[k][i] * D[j].v + 1LL * HASH[D[j].num][i] * Z[i] + 1LL * D[j].v * Z[i]) % Mo[i];
}
}
bool Good(I p, I x, I q, I y) {
F(i, 0, 4) if (HASH[x][i] ^ HASH[y][i]) return 1;
return 0;
}
void GetAns(I k, I fa) {
ans[k] = 1;
for (I x = Ls[k] ; x ; x = Nx[x])
if (To[x] ^ fa) GetAns(To[x], k), ans[k] = (1LL * ans[k] * ans[To[x]]) % mo;
for (bz = 1, len = 0, x = Ls[k]; x; x = Nx[x])
if (To[x] ^ fa) H[++ len] = To[x];
if (k > n) {
F(i, 1, len / 2)
if (Good(rt, H[i], rt, H[len - i + 1])) { bz = false; break; }
if (bz && (len / 2)) ans[k] = (ans[k] * 2LL) % mo;
}
else
{
F(i, 1, len) D[i] = {HASH[H[i]][0], H[i]};
sort(D + 1, D + len + 1, cmp), cnt = 1;
F(i, 2, len)
if (!Good(rt, D[i].num, rt, D[i - 1].num)) cnt ++; else ans[k] = (1LL * ans[k] * jc[cnt]) % mo, cnt = 1;
ans[k] = (1LL * ans[k] * jc[cnt]) % mo;
}
}
void GotHash(I k, I fa) { I y = 0;
for (I x = Ls[k]; x ; x = Nx[x])
if (To[x] ^ fa) GotHash(To[x], k); else y = x; //像我刚刚说的一样,从父亲那条边开始找起来,huhuhu
for (len = 0, x = Nx[y]; x ; x = Nx[x])
D[++ len] = { hash[To[x]][0], To[x] };
for (x = Ls[k] ; x ^ y; x = Nx[x])
if (To[x] ^ fa) D[++ len] = { hash[To[x]][0], To[x] };
if (k <= n) { //圆点和方点分开讨论哟
sort(D + 1, D + len + 1, cmp);
F(i, 0, 4) {
hash[k][i] = 1;
F(j, 1, len) hash[k][i] = (1LL * hash[k][i] * D[j].v + 1LL * hash[D[j].num][i] * Z[i] + 1LL * D[j].v * Z[i]) % Mo[i];
}
}
else
F(i, 0, 4) {
x = y = 1;
F(j, 1, len) x = (1LL * x * D[j].v + 1LL * hash[D[j].num][i] * Z[i] + 1LL * D[j].v * Z[i]) % Mo[i];
G(j, len, 1) y = (1LL * y * D[j].v + 1LL * hash[D[j].num][i] * Z[i] + 1LL * D[j].v * Z[i]) % Mo[i];
hash[k][i] = min(x, y);
}
}
int main() {
R(n), R(m), tot = 1, New = n;
F(i, 1, m) R(x), R(y), ins(x, y), ins(y, x);
Tarjan(1, 0), GetHash(1, 0);
jc[0] = 1; F(i, 1, n) jc[i] = (1LL * jc[i - 1] * i) % mo;
rt = 1, GetAns(rt, 0), answer = ans[rt], cnt = 1;
//一下就是求同构仙人球的个数了
GotHash(rt, 0), mec(Ans, hash);
F(i, 1, n) {
if (i == rt) continue;
GotHash(i, 0), bz = 0;
F(j, 0, 4)
if (hash[i][j] ^ Ans[rt][j]) { bz = 1; break; }
if (!bz) cnt ++;
}
printf("%d
", (1LL * answer * cnt) % mo);
}