bzoj4671 異或圖(容斥原理 + 第二類斯特林數 + 高斯消元)

bzoj4671 異或圖

原題地址http://www.lydsy.com/JudgeOnline/problem.php?id=4671

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

數據範圍
2 ≤ n ≤ 10,1 ≤ s ≤ 60

題解:
好題。
n的範圍是10,那麼邊的範圍爲50,顯然不能直接枚舉邊的子集再看是否聯通。
而考慮到10的貝爾數是一個可枚舉的範圍,可以枚舉點的劃分計算方案然後容斥。
即,不同集合內的點沒有邊,相同集合內的點任意連的方案數,
對於這個方案數,我們只需要跑個高斯消元,解的數量就是2(s|B|)

(關於2(s|B|) 我的理解是和線性基相同,最後消出來得到的s|B| 個自由元,對於每個數(在此處二進制位上表示的是每個圖的存在情況),表示的方法是唯一的。後面被消爲0的數一定可以保留的主元表示出來,2(s|B|) 即異或爲0的集合數量,與原來那一個解異或得到的值相同,2(s|B|) 就是解的數量)
UPD:我是在誤人子弟了…實際上是完全不一樣的。對於此題,可以說是最後剩多少個主元,就有多少個圖的存在性是確定的,那麼剩下的隨意有無就是2(s|B|)

對於容斥係數我們得到:
i=1m{ im}f(i)=[m==1]
f(i) 可以m2 遞推。(或者打個表發現f(i)=(1)i1(i1)! ,代回去可以證出來)

於是用貝爾數的時間枚舉子集劃分,然後跑高消,乘上對應的容斥係數計入答案即可,O(B(n)n2s)
( 注意:此代碼的高消部分存在問題,暫未改正,請勿參考)
代碼:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int N=15;
const int SS=61;
char str[105];
int g[SS][N][N],s,n,S[N][N],f[N],col[N];
LL pw[SS],ans=0,C[SS];
void dfs(int x,int sz)
{
    if(x==n+1)
    {
        int tot=0;
        for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
        {
            LL cur=0; 
            if(col[i]!=col[j])
            {
                for(int ss=1;ss<=s;ss++)
                if(g[ss][i][j]) cur+=pw[ss-1];
                for(int k=1;k<=tot;k++)
                if((cur^C[k])<cur) cur=cur^C[k];
                if(cur) C[++tot]=cur;
            }
        }
        ans+=1LL*f[sz]*pw[s-tot];
        return;
    }
    for(int i=1;i<=sz;i++) {col[x]=i; dfs(x+1,sz);}
    col[x]=sz+1; dfs(x+1,sz+1);
}
int main()
{
    scanf("%d",&s);
    for(int ss=1;ss<=s;ss++)
    {
        scanf("%s",str); int len=strlen(str);
        for(n=1;;n++) if(n*(n-1)==2*len) break;
        for(int l=0,i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
        g[ss][i][j]=g[ss][j][i]=str[l++]-'0';
    }
    for(int i=0;i<=s;i++) pw[i]=1LL<<i;
    for(int i=0;i<=n;i++) for(int j=0;j<=i;j++)
    if(i==j) S[i][j]=1; else S[i][j]=S[i-1][j-1]+j*S[i-1][j];
    f[1]=1;
    for(int i=2;i<=n;i++)
    for(int j=1;j<i;j++) f[i]-=f[j]*S[i][j];    
    dfs(1,0);
    printf("%lld\n",ans);
    return 0;
}
發佈了203 篇原創文章 · 獲贊 43 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章