題庫連接php
給定 \(s\) 個結點數相同且爲 \(n\) 的圖 \(G_1\sim G_s\) ,設 \(S = \{G_1, G_2,\cdots , G_s\}\) ,問 \(S\) 有多少個子集的異或爲一個連通圖。c++
\(1\leq n\leq 10,1\leq s\leq 60\)spa
不妨記 \(f_x\) 爲連通塊個數至少爲 \(x\) 的方案數, \(g_x\) 爲連通塊剛好爲 \(x\) 的方案數。code
容易獲得:ip
\[f_x=\sum_{i=x}^n\begin{Bmatrix}i\\x\end{Bmatrix}g_i\]get
其中第二類斯特林數的含義是將 \(i\) 個連通塊塞成 \(x\) 個的方案數。至於爲何要塞成 \(x\) 個,這和 \(f\) 的計算方式有關,以後會提到。it
那麼由斯特林反演io
\[g_x=\sum_{i=x}^n(-1)^{i-x}\begin{bmatrix}i\\x\end{bmatrix}f_i\]class
那麼im
\[\begin{aligned}g_1&=\sum_{i=1}^n(-1)^{i-1}\begin{bmatrix}i\\1\end{bmatrix}f_i\\&=\sum_{i=1}^n(-1)^{i-1}(i-1)!f_i\end{aligned}\]
考慮如何求 \(f\) ,咱們能夠去枚舉子集劃分,對於橫跨兩個集合的邊,咱們必須讓他們異或爲 \(0\) ,集合內的邊能夠隨意連,咱們不用管(這樣可能會致使集合內不連通,這就是上面第二類斯特林數的含義)。
這樣咱們能夠用 \(O(bell(n))\) 的時間枚舉子集劃分。而後對於每個劃分,用線性基找出邊集的極大線性無關組個數,記爲 \(tot\) ,那麼在當前集合劃分下方案爲 \(2^{s-tot}\) 。
總複雜度 \(O(bell(n)s\frac{n(n-1)}{2})\) 。
#include <bits/stdc++.h> #define ll long long using namespace std; char ch[120]; ll t, x, bin[64], fac[64], ans, base[64], mp[64]; int s, n, belong[64]; void cal(int sz) { t = 0; int cnt = 0; for (int i = 0; i < 64; i++) base[i] = 0; for (int i = 1, l = -1; i <= n; i++) for (int j = i+1; j <= n; j++) t |= bin[++l]*(belong[i] != belong[j]); for (int j = 1; j <= s; j++) { x = t&mp[j]; for (int i = 63; i >= 0; i--) if (x&bin[i]) { if (!base[i]) {base[i] = x; ++cnt; break; } else x ^= base[i]; } } if (sz&1) ans += fac[sz-1]*bin[s-cnt]; else ans -= fac[sz-1]*bin[s-cnt]; } void dfs(int x, int sz) { if (x > n) {cal(sz); return; } for (int i = 1; i <= sz+1; i++) belong[x] = i, dfs(x+1, sz+(i == sz+1)); } void work() { scanf("%d", &s); bin[0] = fac[0] = 1; for (int i = 1; i <= 10; i++) fac[i] = 1ll*fac[i-1]*i; for (int i = 1; i < 64; i++) bin[i] = bin[i-1]<<1; scanf("%s", ch+1); for (int len = strlen(ch+1); n*(n-1)/2 < len; ++n); for (int i = 1, t = 0; i <= n; i++) for (int j = i+1; j <= n; j++) if (ch[++t] == '1') mp[1] |= bin[t-1]; for (int T = 2; T <= s; T++) { scanf("%s", ch+1); for (int i = 1, t = 0; i <= n; i++) for (int j = i+1; j <= n; j++) if (ch[++t] == '1') mp[T] |= bin[t-1]; } dfs(1, 0); printf("%lld\n", ans); } int main() {work(); return 0; }