1. 程式人生 > >【XSY2701】異或圖 線性基 容斥原理

【XSY2701】異或圖 線性基 容斥原理

post line %d 每一個 題目 spa sca type 而在

題目描述

  定義兩個圖\(G_1\)\(G_2\)的異或圖為一個圖\(G\),其中圖\(G\)的每條邊在\(G_1\)\(G_2\)中出現次數和為\(1\)

  給你\(m\)個圖,問你這\(m\)個圖組成的集合有多少個子集的異或圖為一個連通圖。

  \(n\leq 10,m\leq 60\)

題解

  考慮枚舉圖的子集劃分,讓被劃分到不同子集的點之間沒有連邊,而在同一個子集裏面的點可以連通,可以不連通。

  可以用高斯消元(線性基)得到滿足條件的圖的個數。設枚舉的子集劃分有\(k\)個集合,那麽容斥系數就是\({(-1)}^{k-1}(k-1)!\)。並把當前的方案數乘以容斥系數計入答案。

  那麽容斥系數是怎麽來的呢?

  記\(c_i\)\(i\)個集合的容斥系數。對於每一個聯通塊個數為\(j\)的圖,對枚舉到的聯通塊個數為\(i\)的方案有\(S(j,i)\)的貢獻。

  我們只需要讓\(\sum_{i=m}^nc(i)S(i,m)=[m=1]\)就可以了。

  可以打表消元消除容斥系數。

  時間復雜度:\(O(B_nn^2m)\),其中\(B_n\)是Bell數的第\(n\)項。

代碼

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef
long long ll; typedef unsigned long long ull; char s[1010]; int n,m; ull a[20][20]; int d[20]; ull ans=0; ull pw[70]; ull fac[70]; ull c[70]; void dfs(int x,int y) { if(x>n) { int i,j,k; for(i=0;i<m;i++) c[i]=0; for(i=1;i<=n;i++) for(j=i+1;j<=n;j++) if
(d[i]!=d[j]) { ll s=a[i][j]; for(k=m-1;k>=0;k--) if(s&(1ll<<k)) { if(!c[k]) { c[k]=s; break; } s^=c[k]; } } int num=0; for(i=0;i<m;i++) if(!c[i]) num++; ans+=pw[num]*fac[y-1]*(y&1?1:-1); return; } int i; for(i=1;i<=y;i++) { d[x]=i; dfs(x+1,y); } d[x]=y+1; dfs(x+1,y+1); } int main() { #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); freopen("a.out","w",stdout); #endif scanf("%d",&m); int i,j,k; int len; fac[0]=1; pw[0]=1; for(i=1;i<=m;i++) pw[i]=pw[i-1]<<1; for(i=1;i<=m;i++) { scanf("%s",s+1); if(i==1) { len=strlen(s+1); for(j=2;j<=10;j++) if(j*(j-1)/2==len) break; n=j; } int t=0; for(j=1;j<=n;j++) for(k=j+1;k<=n;k++) if(s[++t]=='1') a[j][k]|=1ll<<(i-1); } for(i=1;i<=n;i++) fac[i]=fac[i-1]*i; dfs(1,0); printf("%llu\n",ans); return 0; }

【XSY2701】異或圖 線性基 容斥原理