【BZOJ4671】異或圖(斯特林反演)

【BZOJ4671】異或圖(斯特林反演)

題面

BZOJhtml

Description

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

Input

第一行爲一個整數s, 表圖的個數.
接下來每個二進制串, 第 i 行的二進制串爲 gi, 其中 gi 是原圖經過如下僞代碼轉化得
到的. 圖的結點從 1 開始編號, 下面設結點數爲 n.spa

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

$ 2 ≤ n ≤ 10,1 ≤ s ≤ 60.$code

Output

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

Sample Input
3blog

1ip

1get

0
Sample Output
4input

題解

連通很難處理,正難則反,考慮總方案減去不連通。
總方案隨便算,要考慮的只有不連通的方案數。
若是不連通的話,咱們考慮其子集的劃分,子集之間一定不存在邊連通。那麼咱們考慮其連通塊個數,設\(f_i\)表示剛好有\(i\)個連通塊的方案數,\(g_i\)表示至少有\(i\)個連通塊的劃分的計算的結果。
考慮二者之間的關係:
\[g_k=\sum_{i=k}^n \begin{Bmatrix}i\\k\end{Bmatrix}f_i\]
緣由很簡單,若是直接考慮枚舉連通塊數量考慮劃分的話,由於一個隨意的連通塊可能由多個連通塊組成,意味着會被反覆計算屢次。那麼對於\(k\)個連通塊而言,其被計算的次數就是\(\displaystyle \begin{Bmatrix}i\\k\end{Bmatrix}\)。這個次數是考慮計算至少\(m\)的時候,多出來的連通塊會被劃分到其餘連通塊裏面去,實際上等價於把當前實際存在的\(i\)個連通塊劃分爲\(k\)個計算到至少的貢獻裏面去。
根據斯特林反演,能夠獲得:
\[f_k=\sum_{i=k}^n(-1)^{i-k}\begin{bmatrix}i\\k\end{bmatrix}g_i\]
咱們要求的是剛好一個連通塊的方案數,即\(f_1\)
\[\begin{aligned} f_1&=\sum_{i=1}^n(-1)^{i-1}\begin{bmatrix}i\\1\end{bmatrix}g_i\\ &=\sum_{i=1}^n(-1)^{i-1}(i-1)!g_i \end{aligned}\]
考慮如何計算\(g\)。如今要經過\(g\)來計算\(f\)。因此咱們顯然須要找到一個方法可以直接計算\(g\)
回到題目給定的條件,發現點數不多而圖的數量不少。枚舉一個子集劃分,由於所求是至少,因此只須要肯定不一樣子集之間不存在邊。把這些邊摳出來,要求的就是知足這些位置異或起來爲\(0\)的方案數,構建線性基,答案顯然是\(2^{s-c}\),其中\(c\)是線性基內元素的個數。那麼所有累加到\(g\)中去最後反演計算\(f\)便可。string

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define ll long long
#define MAX 65
#define MAXN 15
int G[MAX][MAXN][MAXN];
char ch[MAX];
int n,K,a[MAXN];
ll ans,p[MAX],jc[MAXN];
void dfs(int x,int t)
{
    if(x==n+1)
    {
        memset(p,0,sizeof(p));int ele=0;
        for(int k=1;k<=K;++k)
        {
            ll S=0;int tot=0;
            for(int i=1;i<=n;++i)
                for(int j=i+1;j<=n;++j)
                    if(a[i]!=a[j])S|=(1ll<<tot)*G[k][i][j],++tot;
            for(int i=0;i<tot;++i)
                if(S&(1ll<<i))
                {
                    if(!p[i]){p[i]=S;++ele;break;}
                    else S^=p[i];
                }
        }
        ans+=1ll*((t&1)?1:-1)*jc[t-1]*(1ll<<(K-ele));
        return;
    }
    for(int i=1;i<=t+1;++i)
        a[x]=i,dfs(x+1,max(i,t));
}
int main()
{
    scanf("%d",&K);
    for(int i=1;i<=K;++i)
    {
        scanf("%s",ch+1);int l=strlen(ch+1),cnt=0;
        for(int j=1;!n;++j)if(j*(j-1)==l+l)n=j;
        for(int j=1;j<=n;++j)
            for(int k=j+1;k<=n;++k)
                G[i][j][k]=ch[++cnt]-48;
    }
    jc[0]=1;for(int i=1;i<=n;++i)jc[i]=jc[i-1]*i;
    dfs(1,0);
    printf("%lld\n",ans);
    return 0;
}
相關文章
相關標籤/搜索