非标定的n阶无向图的非同构个数
特别鸣谢wp师兄跟我的讲解
问题分析:
1. 先不考虑同构,只管标定图的数量
首先我们先从标定图开始看,总共有多少个n阶标定图?
其实这就相当于固定了点数,数边的数量。共有
n
(
n
?
1
)
/
2
n(n-1)/2
n(n?1)/2 条边,那么总共就有
2
n
(
n
?
1
)
2
2^{\frac{n(n-1)}{2}}
22n(n?1)?个标定图(最后发现这个数量并没有用
2. 考虑同构,并将这些标定图进行分组
接下来我们来将这些图分类,我们不妨设共有
t
t
t 个不同构的图记作
G
1
,
G
2
,
.
.
.
,
G
t
G_1,G_2,...,G_t
G1?,G2?,...,Gt?,然后我们再设一个集合,取出所有标定图中与其同构的图,共同放入这个集合中,记作
S
{
G
i
}
S\{G_i\}
S{Gi?}:
S
{
G
1
}
=
{
G
11
,
G
12
,
G
13
,
.
.
.
,
G
1
k
1
}
S\{G_1\}=\{G_{11},G_{12},G_{13},...,G_{1k_1}\}
S{G1?}={G11?,G12?,G13?,...,G1k1??}
S
{
G
2
}
=
{
G
21
,
G
22
,
G
23
,
.
.
.
,
G
2
k
2
}
S\{G_2\}=\{G_{21},G_{22},G_{23},...,G_{2k_2}\}
S{G2?}={G21?,G22?,G23?,...,G2k2??}
.
.
.
...
...
S
{
G
t
}
=
{
G
t
1
,
G
t
2
,
G
t
3
,
.
.
.
,
G
t
k
t
}
S\{G_t\}=\{G_{t1},G_{t2},G_{t3},...,G_{tk_t}\}
S{Gt?}={Gt1?,Gt2?,Gt3?,...,Gtkt??}
因为这些图之间都是不同的,我们可以得到
∑
i
=
1
t
∣
S
{
G
i
}
∣
=
2
n
(
n
?
1
)
2
\sum_{i=1}^t|S\{G_i\}|=2^{\frac{n(n-1)}{2}}
∑i=1t?∣S{Gi?}∣=22n(n?1)?
3. 观察被分到一起的标定图的组
然后我们取出其中一个集合进行观察,例如
S
{
G
1
}
=
{
G
11
,
G
12
,
G
13
,
.
.
.
,
G
1
k
1
}
S\{G_1\}=\{G_{11},G_{12},G_{13},...,G_{1k_1}\}
S{G1?}={G11?,G12?,G13?,...,G1k1??}
首先给定一个
G
1
G_1
G1?的非标定图,你给他任意标定,一定是与
G
1
G_1
G1?同构的。或者换一个说法,对于集合中的第一个元素
G
11
G_{11}
G11?,你对它的顶点做任意的置换,得到的图
G
11
′
G_{11}'
G11′? 一定是与
G
11
G_{11}
G11? 同构的。
但是这里要注意,不是每次置换都会得到一个新的标定图。
例如下图中,左边为
G
11
G_{11}
G11? 其六种置换中有两个与自身相同的,剩下的四种置换也只能得到两个不同的标定图。因此在这个例子中
∣
S
{
G
1
}
∣
=
3
|S\{G_1\}|=3
∣S{G1?}∣=3
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eWPhoRRN-1639494629532)(C:\Users\user\AppData\Roaming\Typora\typora-user-images\image-20211214170228179.png)]
因此,
S
{
G
1
}
S\{G_1\}
S{G1?} 中就是
G
11
G_{11}
G11? 的置换去同构之后剩下的部分。
那么究竟有多少个置换呢?我们用集合
S
i
j
S_{ij}
Sij?表示从
G
1
i
G_{1i}
G1i?变成
G
1
j
G_{1j}
G1j?的所有置换。例如
S
12
S_{12}
S12?表示从
G
11
G_{11}
G11? 可以映射到
G
12
G_{12}
G12? 的所有可能的映射
我们知道所有的置换数相加应该等于
n
!
n!
n!,那么我们以
G
11
G_{11}
G11? 为置换的作用图,可以得到:
∑
i
=
1
k
1
∣
S
1
i
∣
=
n
!
\sum_{i=1}^{k_1}|S_{1i}|=n!
∑i=1k1??∣S1i?∣=n!
通过上面的例子观察可以发现一个奇妙但是又比较自然的规律:
∣
S
11
∣
=
∣
S
12
∣
=
∣
S
13
∣
=
.
.
.
|S_{11}|=|S_{12}|=|S_{13}|=...
∣S11?∣=∣S12?∣=∣S13?∣=...
即从
G
11
G_{11}
G11? 出发得到自身和得到其他图的映射的数目相等。
那么接下来我们对它进行一个证明:
首先设我们的
∣
S
11
∣
=
x
.
S
11
=
{
f
1
,
f
2
,
.
.
.
,
f
x
}
|S_{11}| = x. S_{11}=\{f_1,f_2,...,f_x\}
∣S11?∣=x.S11?={f1?,f2?,...,fx?},即我们自己置换到自己的数量为
x
x
x,接着我们来尝试推
S
12
S_{12}
S12? 的数目
首先,我们一定会存在一个映射
f
f
f,使得
f
?
G
11
=
G
12
f\cdot G_{11}=G_{12}
f?G11?=G12?
那么我们可以得到
?
f
i
∈
S
11
,
??
f
?
f
i
?
G
11
=
G
12
\forall f_i\in S_{11},\ \ f\cdot f_i\cdot G_{11} = G_{12}
?fi?∈S11?,??f?fi??G11?=G12?,同时
f
i
f_i
fi?互不相同可以推出
f
?
f
i
f\cdot f_i
f?fi?也互不相同,所以每有一个可以到自己的置换
f
i
f_i
fi?,就存在一个到
G
12
G_{12}
G12?的置换
f
?
f
i
f\cdot f_i
f?fi?,并且置换之间互不相同
那么也就是说,
?
f
i
∈
S
11
,
??
f
?
f
i
∈
S
12
\forall f_i\in S_{11},\ \ f\cdot f_i\in S_{12}
?fi?∈S11?,??f?fi?∈S12?,因此
∣
S
12
∣
≥
∣
S
11
∣
|S_{12}|\ge|S_{11}|
∣S12?∣≥∣S11?∣
接着我们设
S
12
=
{
g
1
,
g
2
,
.
.
.
,
g
y
}
S_{12}=\{g_1,g_2,...,g_y\}
S12?={g1?,g2?,...,gy?}
那么
S
12
S_{12}
S12? 中的映射满足
?
g
i
∈
S
12
,
??
g
i
?
G
11
=
G
12
\forall g_i\in S_{12},\ \ g_i\cdot G_{11} = G_{12}
?gi?∈S12?,??gi??G11?=G12?,同时对于刚刚的满足
f
?
G
11
=
G
12
f\cdot G_{11}=G_{12}
f?G11?=G12? 的映射
f
f
f,我们可以找到它的逆映射
f
?
1
f^{-1}
f?1,那么我们就有
f
?
1
?
G
12
=
G
11
f^{-1}\cdot G_{12}=G_{11}
f?1?G12?=G11?
那么我们可以得到
?
g
i
∈
S
12
,
??
f
?
1
?
g
i
?
G
11
=
G
11
\forall g_i\in S_{12},\ \ f^{-1}\cdot g_i\cdot G_{11} = G_{11}
?gi?∈S12?,??f?1?gi??G11?=G11?,同时
g
i
g_i
gi?互不相同可以推出
f
?
1
?
g
i
f^{-1}\cdot g_i
f?1?gi?也互不相同,所以每有一个可以到
G
12
G_{12}
G12?的置换$ g_i
,
就
存
在
一
个
到
自
己
的
置
换
,就存在一个到自己的置换
,就存在一个到自己的置换f^{-1}\cdot g_i$,并且置换之间互不相同
那么也就是说,
?
g
i
∈
S
12
,
??
f
?
1
?
g
i
∈
S
11
\forall g_i\in S_{12},\ \ f^{-1}\cdot g_i\in S_{11}
?gi?∈S12?,??f?1?gi?∈S11?,因此
∣
S
12
∣
≤
∣
S
11
∣
|S_{12}|\le|S_{11}|
∣S12?∣≤∣S11?∣
综上可得:
∣
S
11
∣
=
∣
S
12
∣
|S_{11}|=|S_{12}|
∣S11?∣=∣S12?∣
同理可得:
∣
S
11
∣
=
∣
S
12
∣
=
∣
S
13
∣
=
.
.
.
=
∣
S
1
k
1
∣
|S_{11}|=|S_{12}|=|S_{13}|=...=|S_{1k_1}|
∣S11?∣=∣S12?∣=∣S13?∣=...=∣S1k1??∣
∣
S
22
∣
=
∣
S
21
∣
=
∣
S
23
∣
=
.
.
.
=
∣
S
2
k
1
∣
|S_{22}|=|S_{21}|=|S_{23}|=...=|S_{2k_1}|
∣S22?∣=∣S21?∣=∣S23?∣=...=∣S2k1??∣
.
.
.
...
...
∣
S
k
1
k
1
∣
=
∣
S
k
1
1
∣
=
∣
S
k
1
2
∣
=
.
.
.
=
∣
S
k
1
k
1
?
1
∣
|S_{k_1k_1}|=|S_{k_11}|=|S_{k_12}|=...=|S_{k_1k_1-1}|
∣Sk1?k1??∣=∣Sk1?1?∣=∣Sk1?2?∣=...=∣Sk1?k1??1?∣
同时,只需要一组逆映射,则可以推出
∣
S
i
j
∣
=
∣
S
j
i
∣
|S_{ij}|=|S_{ji}|
∣Sij?∣=∣Sji?∣,即从图
G
1
i
G_{1i}
G1i? 到图
G
1
j
G_{1j}
G1j? 的映射数量一定等于从图
G
1
j
G_{1j}
G1j? 到图
G
1
i
G_{1i}
G1i? 的映射数量
因此我们可以将那个从
G
11
G_{11}
G11?出发的式子转化为
∑
i
=
1
k
1
∣
S
i
i
∣
=
n
!
\sum_{i=1}^{k_1}|S_{ii}|=n!
∑i=1k1??∣Sii?∣=n!
4. 回到组与组之间
我们对第一组进行了一定的分析,得到了上面的式子
∑
i
=
1
k
1
∣
S
i
i
∣
=
n
!
\sum_{i=1}^{k_1}|S_{ii}|=n!
∑i=1k1??∣Sii?∣=n!
为了方便区分这是第几组,我们给
S
S
S 加上上标
(
1
)
^{(1)}
(1) :
∑
i
=
1
k
1
∣
S
i
i
(
1
)
∣
=
n
!
\sum_{i=1}^{k_1}|S_{ii}^{(1)}|=n!
∑i=1k1??∣Sii(1)?∣=n!
那么对于所有的
t
t
t 组同构所构成的集合,我们都可以得到一个这样的等式:
?
1
≤
p
≤
t
,
∑
i
=
1
k
p
∣
S
i
i
(
p
)
∣
=
n
!
\forall 1\le p\le t,\sum_{i=1}^{k_p}|S_{ii}^{(p)}|=n!
?1≤p≤t,∑i=1kp??∣Sii(p)?∣=n!
那我们将这里所有的等式加起来就可以得到:
∑
p
=
1
t
∑
i
=
1
k
p
∣
S
i
i
(
p
)
∣
=
t
?
n
!
t
=
∑
p
=
1
t
∑
i
=
1
k
p
∣
S
i
i
(
p
)
∣
n
!
\sum_{p=1}^{t}\sum_{i=1}^{k_p}|S^{(p)}_{ii}|=t*n!\\ t=\frac{\sum_{p=1}^{t}\sum_{i=1}^{k_p}|S^{(p)}_{ii}|}{n!}
p=1∑t?i=1∑kp??∣Sii(p)?∣=t?n!t=n!∑p=1t?∑i=1kp??∣Sii(p)?∣? 而我们回顾我们分组的过程,就可以发现
∑
p
=
1
t
∑
i
=
1
k
p
\sum_{p=1}^{t}\sum_{i=1}^{k_p}
∑p=1t?∑i=1kp?? 其实就是对所有的标定图
G
G
G,
∣
S
i
i
(
p
)
∣
|S^{(p)}_{ii}|
∣Sii(p)?∣ 就是对于这个图
G
G
G 它的能置换到自己的置换的数目。
那么这个求和,其实就是找到所有的特定的置换与图组成的二元组
(
f
,
G
)
(f, G)
(f,G),其中
f
?
G
=
G
f\cdot G=G
f?G=G
那么在计算机中,枚举每个图的代价是巨大的,正如之前算的是
2
n
(
n
?
1
)
2
2^{\frac{n(n-1)}{2}}
22n(n?1)?,但是我们换一种思路,我们可以来枚举所有的置换
f
f
f,找到所有的满足
f
?
G
=
G
f\cdot G=G
f?G=G 的图G,设
S
G
{
f
}
S_G\{f\}
SG?{f} 表示满足的图的集合,这样我们可以得到下面的式子
t
=
∑
f
∣
S
G
{
f
}
∣
n
!
t=\frac{\sum_f|S_G\{f\}|}{n!}
t=n!∑f?∣SG?{f}∣? 而枚举置换的复杂度就只有
O
(
n
!
)
O(n!)
O(n!) 了(其实和之前那个一样都是指数级的,只是那个显然更高阶
5. 计算给定了置换
f
f
f 之后有多少个图满足
f
?
G
=
G
f\cdot G=G
f?G=G
首先我们知道图是通过边集的来定义的
而这里的点集的置换一定对应一个边集的置换
例如
{
1
,
2
,
3
,
4
}
→
{
2
,
3
,
1
,
4
}
\{1,2,3,4\} \rightarrow \{2,3,1,4\}
{1,2,3,4}→{2,3,1,4} 的点集置换
f
f
f 就可以得到一个相应的边集置换
f
′
f'
f′:
{
(
1
,
2
)
,
(
1
,
3
)
,
(
1
,
4
)
,
(
2
,
3
)
,
(
2
,
4
)
,
(
3
,
4
)
}
→
{
(
2
,
3
)
,
(
1
,
2
)
,
(
2
,
4
)
,
(
1
,
3
)
,
(
3
,
4
)
,
(
1
,
4
)
}
\{(1,2), (1,3), (1,4), (2,3), (2,4), (3,4)\} \rightarrow \{(2,3), (1,2), (2,4), (1,3), (3,4), (1,4)\}
{(1,2),(1,3),(1,4),(2,3),(2,4),(3,4)}→{(2,3),(1,2),(2,4),(1,3),(3,4),(1,4)}
那么我们考虑这个点集所对应的满足自己映射到自己的所有的图,其实就是看这个边集置换下的能映射到自己的图:
那么我们就需要找到的图会在拥有了一条边之后,就一定拥有了其映射之后得到的另一条边,以此类推,直至形成环形。
那么我们先选择集合中的一条边
e
1
e_1
e1?对其进行边集置换得到
e
2
e_2
e2?,再依次进行,直到得到第一个等于之前出现过的边
e
k
+
1
e_{k+1}
ek+1?为止,这样我们就得到了
k
k
k 条不同的边构成了一个置换的循环。然后继续选择集合中不同于这
k
k
k 条边的其它边几次进行这个操作。
首先我们可以证明
e
k
+
1
=
e
1
e_{k+1}=e_1
ek+1?=e1?
因为假设
e
k
+
1
=
e
i
,
(
i
>
1
)
e_{k+1}=e_i,(i>1)
ek+1?=ei?,(i>1),那么根据
f
′
?
e
i
?
1
=
e
i
f'\cdot e_{i-1}=e_i
f′?ei?1?=ei?,就有
f
′
?
1
?
e
i
=
e
i
?
1
f'^{-1}\cdot e_i=e_{i-1}
f′?1?ei?=ei?1?,那么根据
e
i
=
e
k
+
1
e_i=e_{k+1}
ei?=ek+1? 就有
f
′
?
1
?
e
k
+
1
=
e
i
?
1
f'^{-1}*e_{k+1}=e_{i-1}
f′?1?ek+1?=ei?1? 也即
e
k
=
e
i
?
1
e_k=e_{i-1}
ek?=ei?1? 那么违反了
e
k
+
1
e_{k+1}
ek+1?是第一个与之前重复的假设。所以
e
k
+
1
e_{k+1}
ek+1? 一定等于
e
1
e_1
e1?。
例如上面这个例子:
我们先选择
(
1
,
2
)
(1,2)
(1,2) 这条边
(
1
,
2
)
→
(
2
,
3
)
→
(
1
,
3
)
→
(
1
,
2
)
{(1,2) \rightarrow (2,3) \rightarrow (1,3) \rightarrow (1,2)}
(1,2)→(2,3)→(1,3)→(1,2)
再选择
(
1
,
4
)
(1,4)
(1,4) 这条边
(
1
,
4
)
→
(
2
,
4
)
→
(
3
,
4
)
→
(
1
,
4
)
{(1,4) \rightarrow (2,4) \rightarrow (3,4) \rightarrow (1,4)}
(1,4)→(2,4)→(3,4)→(1,4)
因此我们就得到了两个长度为
3
3
3 的循环节
那么在这样一个映射下,这两个循环节里的边要么都选要么都不选,因此我们一共有
2
2
=
4
2^2=4
22=4 种方法
更加通式的情况,对于一个点集映射
f
f
f,我们可以得到它的边集映射
f
′
f'
f′,在
f
′
f'
f′ 中我们可以通过遍历的方法得到它的循环节个数,设这个个数为
I
(
f
′
)
I(f')
I(f′)
那么对于点集映射
f
f
f 的满足
f
?
G
=
G
f\cdot G=G
f?G=G 的
G
G
G 的个数就等于
2
I
(
f
′
)
2^{I(f')}
2I(f′)
6. 最终公式
t
=
∑
f
2
I
(
f
′
)
n
!
t=\frac{\sum_f2^{I(f')}}{n!}
t=n!∑f?2I(f′)?
7. 实现代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 13;
int n;
long long sum = 0;
long long lv[maxn];
int f[maxn];
bool visEdge[maxn][maxn];
bool vis[maxn];
void calculate(){
int cnt = 0;
memset(visEdge, 0, sizeof(visEdge));
for(int i = 1; i <= n; i++){
for(int j = i+1; j <= n; j++)
if(!visEdge[i][j]){
int ni = i, nj = j;
do{
visEdge[ni][nj] = true;
ni = f[ni], j = f[nj];
if(ni > nj) swap(ni, nj);
}while(visEdge[ni][nj] == false);
cnt ++;
}
}
long long s = 1;
for(int i = 0; i < cnt; i++)
s <<= 1;
sum += s;
}
void search(int t){
if(t == n + 1){
calculate();
return ;
}
for(int i = 1; i <= n; i ++)
if(!vis[i]){
vis[i] = true;
f[t] = i;
search(t + 1);
vis[i] = false;
}
}
int main(){
scanf("%d", &n);
lv[0] = 1;
for(int i = 1; i <= n; i++)
lv[i] = lv[i - 1] * i;
search(1);
printf("%lld", sum/lv[n]);
return 0;
}
|