BZOJ4671 異或圖(容斥+線性基)
題意
定義兩個結點數相同的圖 \(G_1\) 與圖 \(G_2\) 的異或為一個新的圖 \(G\) ,其中如果 \((u, v)\) 在 \(G_1\) 與 \(G_2\) 中的出現次數之和為 \(1\) , 那麼邊 \((u, v)\) 在 \(G\) 中, 否則這條邊不在 \(G\) 中.
現在給定 \(s\) 個結點數相同的圖 \(G_{1...s}\) , 設 \(S = {G_1, G_2, \cdots , G_s}\) , 請問 \(S\) 有多少個子集的異或為一個連通圖?
\(n \le 10, s \le 60\)
題解
原來聽過這題,但一直沒有想去寫,又講了一遍,就來做了下,可是還不會。。。
連通圖計數的一個經典思路就是容斥。
對於這道題,我們先用貝爾數 \(bell(n)\) 的時間來列舉 \(n\) 個點的子集(聯通塊)劃分,強制連通性 至少 是這個劃分。
也就是說,不同子集的兩個點之間一定沒有邊,相同子集的兩個點則任意。
對於一個有 \(m\) 個連通塊的圖。令 \(f_i\) 為至少有 \(i\) 個聯通塊的容斥係數需要滿足
\[ \sum_{i = 1}^{m} {m\brace i} f_i = [m = 1] \]
可以斯特林反演,也可以打表找規律得出
\[ f_i = (-1)^{i - 1} (i - 1)! \]
那麼問題就轉化成,我們只考慮不同子集中的邊。對於 \(s\)
這個顯然是可以利用線性基得到異或方案的,記線性基的元素個數為 \(tot\) ,由於之中的元素是線性無關的,其他的 \(2^{s - tot}\) 個集合是一定可以通過異或(或不異或)線性基裡的某些元素得到 \(0\) 的。
那麼方案數其實就是 \(2^{s - tot}\) 。
因為此題卡常,所以要卡一些常數才能通過此題qwq 具體可以看程式碼實現優化。
程式碼
#include <bits/stdc++.h> #define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i) #define Set(a, v) memset(a, v, sizeof(a)) #define Cpy(a, b) memcpy(a, b, sizeof(a)) #define debug(x) cout << #x << ": " << (x) << endl using namespace std; template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; } template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; } inline int read() { int x(0), sgn(1); char ch(getchar()); for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1; for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48); return x * sgn; } void File() { #ifdef zjp_shadow freopen ("4671.in", "r", stdin); freopen ("4671.out", "w", stdout); #endif } typedef long long ll; const int N = 12, M = 62; int Strl[N][N], id[N][N], n, s, fac[N], coef[N]; bitset<N> E[M][N]; char str[M]; ll base[M], ans; int bel[N]; pair<int, int> ins[M]; void Dfs(int u, int tot) { if (u > n) { Set(base, 0); int res = 0, tmp = -1; For (i, 1, n) For (j, i + 1, n) if (bel[i] != bel[j]) { ins[++ tmp] = make_pair(i, j); } For (i, 1, s) { ll now = 0; For (j, 0, tmp) if (E[i][ins[j].first][ins[j].second]) now |= 1ll << j; Fordown (j, tmp, 0) if (now >> j & 1) { if (base[j]) now ^= base[j]; else { base[j] = now; ++ res; break; } } } ans += coef[tot] * (1ll << (s - res)); return; } For (i, 1, tot + 1) bel[u] = i, Dfs(u + 1, max(tot, i)); } int main () { File(); s = read(); scanf ("%s", str + 1); int len = strlen(str + 1); while (n * (n - 1) / 2 < len) ++ n; For (k, 1, s) { len = 0; For (i, 1, n) For (j, i + 1, n) E[k][i][j] = str[id[i][j] = ++ len] - '0'; if (k < s) scanf ("%s", str + 1); } fac[0] = 1; For (i, 1, n) { fac[i] = 1ll * fac[i - 1] * i; coef[i] = (i & 1 ? 1 : -1) * fac[i - 1]; } Dfs(1, 0); printf ("%lld\n", ans); return 0; }