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的貝爾數是一個可枚舉的範圍,可以枚舉點的劃分計算方案然後容斥。
即,不同集合內的點沒有邊,相同集合內的點任意連的方案數,
對於這個方案數,我們只需要跑個高斯消元,解的數量就是 。
(關於 我的理解是和線性基相同,最後消出來得到的 個自由元,對於每個數(在此處二進制位上表示的是每個圖的存在情況),表示的方法是唯一的。後面被消爲0的數一定可以保留的主元表示出來, 即異或爲0的集合數量,與原來那一個解異或得到的值相同, 就是解的數量)
UPD:我是在誤人子弟了…實際上是完全不一樣的。對於此題,可以說是最後剩多少個主元,就有多少個圖的存在性是確定的,那麼剩下的隨意有無就是 。
對於容斥係數我們得到:
可以 遞推。(或者打個表發現 ,代回去可以證出來)
於是用貝爾數的時間枚舉子集劃分,然後跑高消,乘上對應的容斥係數計入答案即可, 。
( 注意:此代碼的高消部分存在問題,暫未改正,請勿參考)
代碼:
#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;
}