bzoj4671 異或圖 [搜索+線性基+斯特林反演]

Description:
定義兩個結點數相同的圖G1 與圖G2 的異或爲一個新的圖G , 其中如果(u,v)G1
G2 中的出現次數之和爲1 , 那麼邊(u,v)G 中, 否則這條邊不在G 中.
現在給定s 個結點數相同的圖G1...s , 設S=G1,G2,...,Gs , 請問S 有多少個子集的異
或爲一個連通圖?


Solution:
直接枚舉不可做,那麼考慮容斥,計算至少有幾個連通塊進行容斥,設方案數爲fi 。那麼結果就是恰好有i個連通塊,那麼就是gi
那麼花費bell(n) 的時間劃分子集,這些子集之間不能有邊,利用線性基算出自由元數量x ,那麼對應的方案數就是2x
考慮gifi 的關係,有
fm=i=mngiS(i,m)
根據斯特林反演
gm=i=mn(1)ims(i,m)fi
那麼得出g1=i=1n(1)i1(i1)!fi
所以每次計算出fi ,然後套入式子即可。


#include <bits/stdc++.h>
using namespace std;
const int maxn = 11, maxm = 65;
int n, m;
long long ans;
int g[maxm][maxn][maxn], bl[maxn];
long long a[maxm], fac[maxn];
char s[maxm];
void dfs(int dep, int k) {
    if(dep == n) {
        memset(a, 0, sizeof(a));
        for(int i = 1; i <= n; ++i) {
            for(int j = i + 1; j <= n; ++j) {
                if(bl[i] != bl[j]) {
                    long long tmp = 0;
                    for(int u = 1; u <= m; ++u) {
                        tmp |= 1LL * g[u][i][j] << (u - 1);
                    }
                    for(int t = m - 1; ~t; --t) {
                        if(tmp >> t & 1) {
                            if(a[t]) {
                                tmp ^= a[t];
                            } else {
                                a[t] = tmp;
                                break;
                            }
                        }
                    }   
                }   
            }
        }
        int c = 0;
        for(int i = 0; i < m; ++i) {
            c += a[i] > 0; 
        }
        ans += ((k & 1) ? 1 : -1) * (1LL << (m - c)) * fac[k - 1];  
        return;
    }
    for(int i = 1; i <= k + 1; ++i) {
        bl[dep + 1] = i;
        dfs(dep + 1, max(k, i));
    }
}
int main() {
    scanf("%d", &m);
    for(int i = 1; i <= m; ++i) {
        scanf("%s", s + 1);
        int l = strlen(s + 1), t = 0;
        n = (1 + sqrt(1 + (l << 3))) / 2;
        for(int u = 1; u <= n; ++u) {
            for(int v = u + 1; v <= n; ++v) {
                g[i][u][v] = s[++t] - '0';  
            }
        }
    }
    fac[0] = 1;
    for(int i = 1; i <= n; ++i) {
        fac[i] = fac[i - 1] * i;
    }
    dfs(0, 0);
    printf("%lld\n", ans);
    return 0;
} 

發佈了102 篇原創文章 · 獲贊 1 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章