一類帶標號圖的計數
題目大意
求$n$個點的帶標號歐拉圖計數;再稍微轉換一下得到答案。
題目分析
限於一些原因,本篇博客只介紹帶標號歐拉圖計數的操作。
前置芝士:圖$G$為歐拉圖當且僅當$G$為連通圖且$\forall deg_V為偶數$
第一種方法:組合數學
記$f_i$為$i$個點帶標號歐拉圖個數;$g_i$為滿足$i$個點度數均為偶數的圖個數。
對於$g_i$有:$g_i=2^{C ^2_{i-1}}$
意即取出$i-1$個點,對於$C ^2_{i-1}$條邊各自可選可不選;最后使用剩下的$1$個點來平衡所有度數為奇數的點。
對於$f_i$有:$f_i= g_i-\sum\limits^{i-1}_{j=1}f_jg_{i-j} {{i-1}\choose{j-1}}$
后面這個形式是用來避免算重的。如果簡單地$f_i= g_i-\sum\limits^{i-1}_{j=1}f_jg_{i-j} {i\choose{j}}$,將會算重這種情況:
因此,我們可以欽定當前大小為$i$的圖的一個特定點一定在$f_j$所處的連通塊中,也即$j$就是在枚舉特定點所處連通塊的大小。
總結一下,對於以上這個式子:$f_jg_{i-j}$表示的是將大小分別為$j$和$i-j$的帶標號圖合並成大小為$i$的圖的方案數量;${i-1}\choose{j-1}$表示對於組合出來的圖,將其對應到我們所要標號的圖上的方案數。
題外話:對於兩個相對標號獨立的圖的合並,我糾結了很久為什么不乘上$i\choose j$(兩個具有相對大小的集合合並為更大的具有相對大小的集合的方案數)。后來才意識到如果過早地將兩張圖對應標號,也就相當於是將特定點的位置移動了。我們之所以要枚舉所有$j$來計算${i-1}\choose{j-1}$就是為了從這個角度去重。
於是就可以$O(n^2)$遞推了。
1 #include<bits/stdc++.h> 2 #define MO 1000000007 3 const int maxn = 2035; 4 5 int n,fac[maxn],facinv[maxn]; 6 int f[maxn],g[maxn]; 7 8 int qmi(int a, int b) 9 { 10 int ret = 1; 11 for (; b; b>>=1, a=1ll*a*a%MO) 12 if (b&1) ret = 1ll*ret*a%MO; 13 return ret; 14 } 15 int C(int n, int m){return 1ll*fac[n]*facinv[m]%MO*facinv[n-m]%MO;} 16 void init() 17 { 18 fac[0] = fac[1] = facinv[0] = facinv[1] = 1; 19 for (int i=2; i<=2000; i++) 20 fac[i] = 1ll*fac[i-1]*i%MO, 21 facinv[i] = MO-1ll*facinv[MO%i]*(MO/i)%MO; 22 for (int i=2; i<=2000; i++) 23 facinv[i] = 1ll*facinv[i]*facinv[i-1]%MO, 24 f[i] = g[i] = qmi(2, ((i-1)*(i-2))>>1); 25 f[1] = g[1] = 1; 26 } 27 int main() 28 { 29 init(), scanf("%d",&n); 30 for (int i=1; i<=n; i++) 31 for (int j=1; j<i; j++) 32 f[i] = ((f[i]-1ll*f[j]*g[i-j]%MO*C(i-1, j-1))%MO+MO)%MO; 33 printf("%d\n",1ll*f[n]*C(n, 2)%MO); 34 return 0; 35 }
第二種方法:生成函數