定義兩個結點數相同的圖 \(G_1\) 與圖 \(G_2\) 的異或爲一個新的圖 \(G\), 其中若是 \((u, v)\) 在 \(G_1\) 與 \(G_2\) 中的出現次數之和爲 \(1\), 那麼邊 \((u, v)\) 在 \(G\) 中, 不然這條邊不在 \(G\) 中.ios
如今給定 \(s\) 個結點數相同的圖 \(G_{1 \sim s}\), 設 \(S = \{G_1, G_2, \cdots , G_s\}\), 請問 \(S\) 有多少個子集的異或爲一個連通圖?git
第一行爲一個整數 \(s\), 表圖的個數.spa
接下來每個二進制串, 第 \(i\) 行的二進制串爲 \(g_i\), 其中 \(g_i\) 是原圖經過如下僞代碼轉化獲得的. 圖的結點從 \(1\) 開始編號, 下面設結點數爲 \(n\).code
Algorithm 1 Print a graph G = (V, E) for i = 1 to n do for j = i + 1 to n do if G contains edge (i, j) then print 1 else print 0 end if end for end for
輸出一行一個整數, 表示方案數get
3 1 1 0
4
\(2 \le n \le 10,1 \le s \le 60\).string
連通性計數相關的問題通常要用到容斥原理,這是由於「連通」很是難處理,由於總體連通並不知道每條邊的存在狀況,而「不連通」則是能夠肯定沒有任何邊相連;而容斥就是用 \(連通方案 = 總方案 - 不連通方案\),從而將連通計數問題轉化爲不連通計數的問題。it
此題就是考慮計算「不連通」的狀況。咱們須要枚舉節點的子集劃分,不一樣的子集之間的節點沒有任何邊相連,同一子集中的點連邊狀況則沒有限制,令咱們劃分的子集個數爲 \(m\),且在全部 \(m\)-劃分的狀況下計算的方案總和爲 \(f_m\),並令 \(g_m\) 表示剛好有 \(m\) 個連通塊的方案數,顯然 \(f_m \ge g_m\),由於咱們算的還有內部不連通的狀況。io
更加具體一點,實際上是知足這樣一個關係class
\[ f_m = \sum_{i=m}^n \begin{Bmatrix} i \\ m \end{Bmatrix} g_i \]stream
其中 \(\begin{Bmatrix} i \\ m \end{Bmatrix}\) 表示第二類斯特林數「\(i\) 子集 \(m\)」。顯然對於每種 \(g_i(i \ge m)\),\(i\) 個集合之間是沒有考慮順序的,而咱們計算出來的 \(f_m\) 則會將某個多出來的集合併到那 \(m\) 個集合中某個集合裏面去,因此它須要乘上一個子集劃分。
根據斯特林反演能夠獲得
\[ g_m = \sum_{i=m}^n (-1)^{i-m} \begin{bmatrix} i \\ m \end{bmatrix} f_i \]
如今咱們只要求 \(g_1\),因此
\[ g_1 = \sum_{i=1}^n (-1)^{i-1} (i-1)! f_i \]
如今主要問題就是 \(f_m\) 如何求。咱們能夠枚舉子集劃分,而後某些邊的狀態肯定了,這時咱們令 \(x_1, x_2, \cdots , x_s\) 分別表示圖 \(G_1, G_2, \cdots , G_s\) 的選擇狀況(\(x_i = 1\) 表示選擇 \(G_i\),\(x_i = 0\) 表示不選),那麼對於每一條肯定的邊咱們都能獲得一個異或方程,那麼會獲得一個若干個異或方程組成的異或方程組,咱們對它進行高斯消元就能知道主元的個數 \(c\) 了,那麼方案數就是 \(2^{s-c}\),把這個方案數累加到 \(f_m\)(\(m\) 是當前枚舉的子集劃分中子集的個數)中。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> #include <cmath> using namespace std; #define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++) #define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--) int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define maxs 65 #define maxn 15 #define maxe 50 #define LL long long char str[maxe]; int s, n, gr[maxs][maxn][maxn]; LL ans, A[maxe], fac[maxn]; int bl[maxn]; void solve(int cur, int m) { if(cur >= n) { memset(A, 0, sizeof(A)); rep(i, 0, n - 1) rep(j, i + 1, n - 1) if(bl[i] != bl[j]) { LL tmp = 0; rep(g, 0, s - 1) tmp |= (LL)gr[g][i][j] << g; dwn(k, maxe - 1, 0) if(tmp >> k & 1) { if(A[k]) tmp ^= A[k]; else{ A[k] = tmp; break; } } } int c = 0; rep(k, 0, maxe - 1) c += A[k] > 0; ans += ((m & 1) ? 1ll : -1ll) * (1ll << s >> c) * fac[m-1]; return ; } rep(i, 1, m + 1) bl[cur] = i, solve(cur + 1, max(m, i)); return ; } int main() { s = read(); rep(i, 0, s - 1) { scanf("%s", str); int l = strlen(str), t = 0; n = (1 + sqrt(1 + (l << 3))) / 2; rep(x, 0, n - 1) rep(y, x + 1, n - 1) gr[i][x][y] = str[t++] - '0'; } fac[0] = 1; rep(i, 1, n) fac[i] = fac[i-1] * i; solve(0, 0); printf("%lld\n", ans); return 0; }