[BZOJ4671]異或圖


[BZOJ4671]異或圖

試題描述

定義兩個結點數相同的圖 \(G_1\) 與圖 \(G_2\) 的異或為一個新的圖 \(G\), 其中如果 \((u, v)\)\(G_1\)\(G_2\) 中的出現次數之和為 \(1\), 那么邊 \((u, v)\)\(G\) 中, 否則這條邊不在 \(G\) 中.

現在給定 \(s\) 個結點數相同的圖 \(G_{1 \sim s}\), 設 \(S = \{G_1, G_2, \cdots , G_s\}\), 請問 \(S\) 有多少個子集的異或為一個連通圖?

輸入

第一行為一個整數 \(s\), 表圖的個數.

接下來每一個二進制串, 第 \(i\) 行的二進制串為 \(g_i\), 其中 \(g_i\) 是原圖通過以下偽代碼轉化得到的. 圖的結點從 \(1\) 開始編號, 下面設結點數為 \(n\).

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

輸出

輸出一行一個整數, 表示方案數

輸入示例

3
1
1
0

輸出示例

4

數據規模及約定

\(2 \le n \le 10,1 \le s \le 60\).

題解

連通性計數相關的問題一般要用到容斥原理,這是因為“連通”非常難處理,因為整體連通並不知道每條邊的存在情況,而“不連通”則是可以確定沒有任何邊相連;而容斥就是用 \(連通方案 = 總方案 - 不連通方案\),從而將連通計數問題轉化為不連通計數的問題。

此題就是考慮計算“不連通”的情況。我們需要枚舉節點的子集划分,不同的子集之間的節點沒有任何邊相連,同一子集中的點連邊情況則沒有限制,令我們划分的子集個數為 \(m\),且在所有 \(m\)-划分的情況下計算的方案總和為 \(f_m\),並令 \(g_m\) 表示恰好有 \(m\) 個連通塊的方案數,顯然 \(f_m \ge g_m\),因為我們算的還有內部不連通的情況。

更加具體一點,其實是滿足這樣一個關系

\[f_m = \sum_{i=m}^n \begin{Bmatrix} i \\ m \end{Bmatrix} g_i \]

其中 \(\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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM