题意:给定一张n个点,m条边的无向图,问补图的本质不同的哈密顿回路个数
一看数据范围,$m \le 20$,再看题意,计数问题,那么很大概率就是容斥了……
一个显然的容斥:$ans=\sum_{s \subseteq S} (-1)^{\mid s \mid}f_s$,其中$S$表示所有给定边的集合,$f_s$表示至少选择了$s$中的边的哈密顿回路个数
注释:$n$个点的圆排列个数为$\frac{n!}{n}=(n-1)!$
显然有$f_s=\frac{(n-tot+k-1)! \times 2^k}{2}$,其中$tot$表示这些边中有多少个不同的点,$k$表示这些边形成多少条链
注意判断$f_s=0$的情况(无解)
1 #include "bits/stdc++.h" 2 using namespace std; 3 typedef long long ll; 4 const int N = 1e7 + 10, mod = 1e9 + 7; 5 int fa[N], x[N], y[N], n, m, sz[N]; 6 int get(int x) { return x == fa[x] ? x : fa[x] = get(fa[x]); } 7 int cnt[N], vis[N], deg[N], g[N]; 8 ll fac[N]; 9 ll ans; 10 ll pw(ll a, ll b) { 11 ll r = 1; 12 for( ; b ; b >>= 1, a = a * a % mod) if(b & 1) r = r * a % mod; 13 return r; 14 } 15 ll sol(int s) { 16 ll res = 0; 17 int flag = 0, k = 0, sum = 0; 18 for(int i = 1 ; i <= m ; ++ i) { 19 if((s >> (i - 1)) & 1) { 20 int u = x[i], v = y[i]; 21 fa[u] = u, fa[v] = v; 22 vis[u] = 0, vis[v] = 0; 23 deg[u] = 0, deg[v] = 0; 24 g[u] = 0, g[v] = 0; 25 } 26 } 27 for(int i = 1 ; i <= m ; ++ i) { 28 if((s >> (i - 1)) & 1) { 29 int u = x[i], v = y[i]; 30 if(deg[u] == 0) ++ sum; 31 if(deg[v] == 0) ++ sum; 32 if((++ deg[u]) >= 3 || (++ deg[v]) >= 3) return 0; 33 } 34 } 35 for(int i = 1 ; i <= m ; ++ i) { 36 if((s >> (i - 1)) & 1) { 37 int u = get(x[i]), v = get(y[i]); 38 if(u == v) flag = 1; 39 fa[u] = v; 40 } 41 } 42 for(int i = 1 ; i <= m ; ++ i) { 43 if((s >> (i - 1)) & 1) { 44 int u = get(x[i]); 45 if(!g[u]) { 46 g[u] = 1; 47 ++ k; 48 } 49 } 50 } 51 if(flag && (cnt[s] != n || k > 1)) { 52 return 0; 53 } 54 res = fac[n - sum + k - 1] * pw(2, k) % mod * pw(2, mod - 2) % mod; 55 if(cnt[s] & 1) res = -res; 56 return res; 57 } 58 59 int main() { 60 freopen("lighthouse.in", "r", stdin); 61 freopen("lighthouse.out", "w", stdout); 62 scanf("%d%d", &n, &m); 63 for(int i = 0 ; i < (1 << m) ; ++ i) cnt[i] = cnt[i >> 1] + (i & 1); 64 for(int i = fac[0] = 1 ; i <= n ; ++ i) fac[i] = fac[i - 1] * i % mod; 65 for(int i = 1 ; i <= m ; ++ i) scanf("%d%d", &x[i], &y[i]); 66 for(int s = 0 ; s < (1 << m) ; ++ s) { 67 ll t = sol(s); 68 ans = (ans + t) % mod; 69 } 70 ans = (ans % mod + mod) % mod; 71 printf("%lld\n", ans); 72 }