[bzoj4671]異或圖——容斥+斯特林數反演+線性基
阿新 • • 發佈:2019-01-04
題目大意:
定義兩個結點數相同的圖 G1 與圖 G2 的異或為一個新的圖 G, 其中如果 (u, v) 在 G1 與G2 中的出現次數之和為 1, 那麼邊 (u, v) 在 G 中, 否則這條邊不在 G 中.
現在給定 s 個結點數相同的圖 G1...s, 設 S = {G1, G2, . . . , Gs}, 請問 S 有多少個子集的異或為一個連通圖?
思路:
這種計算連通圖的個數的題目一般情況下考慮容斥。
由於一個圖的聯通不好直接計算,但是對於點集的劃分,我們要使它不連通會容易得多。
於是在發現雖然\(s\)較大,但是\(n\)很小的情況下,我們可以列舉這\(n\)個點的劃分,然後對於每一個劃分,強制不同集合中的點不連通,同一個集合中的點任意,然後計算滿足條件的圖的個數。
這個時候我們的容斥係數應該滿足這樣的條件,對於一個擁有\(m\)
\[ \sum_{i=1}^{m}{m\brace i}\times f_i=[m=1] \]
然後考慮直接用斯特林數反演來求出\(f_i:\)
\[ f_i=(-1)^{(i-1)}\times (i-1)! \]
接下來考慮如何計算滿足各個集合中的點不連通的方案數,這裡把跨越集合的邊單獨提出來,每條邊的存在狀況可以看成是一個\(01\)串,現在即求這\(s\)個01串的異或和中有多少個\(0\)。
於是我們可以直接對這\(s\)個數建立線性基,如果最後線性基中有\(c\)個元素,那麼一共有\(2^{s-c}\)種方式可以使異或和為0。
#include<bits/stdc++.h> #define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i) #define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i) #define debug(x) cout<<#x<<"="<<x<<" " #define fi first #define se second #define mk make_pair #define pb push_back typedef long long ll; using namespace std; void File(){ freopen("bzoj4671.in","r",stdin); freopen("bzoj4671.out","w",stdout); } template<typename T>void read(T &_){ _=0; T f=1; char c=getchar(); for(;!isdigit(c);c=getchar())if(c=='-')f=-1; for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0'); _*=f; } const int maxn=10+10; const int maxs=60+10; int s,n,bel[maxn],q[maxs],cnt; char str[maxs][maxs]; ll num[maxs],fac[maxs],f[maxs],ans,b[maxs]; void init(){ read(s); REP(i,1,s)scanf("%s",str[i]+1); int len=strlen(str[1]+1); REP(i,2,10)if(i*(i-1)/2==len)n=i; fac[0]=1; REP(i,1,10)fac[i]=fac[i-1]*i; REP(i,1,10)f[i]=((i-1)%2 ? -1 : 1)*fac[i-1]; } void calc(int tot){ REP(i,1,s){ num[i]=0; REP(j,1,cnt)num[i]=num[i]<<1|(str[i][q[j]]^'0'); } REP(i,1,cnt)b[i]=0; int c=0; REP(i,1,s){ DREP(j,cnt,1){ if((1ll<<(j-1))&num[i]){ if(!b[j]){++c;b[j]=num[i];break;} num[i]^=b[j]; } } } ans+=f[tot]*(1ll<<(s-c)); } void dfs(int k,int tot){ if(k>n){ cnt=0; int id=0; REP(i,1,n)REP(j,i+1,n){ ++id; if(bel[i]!=bel[j]) q[++cnt]=id; } calc(tot); return; } bel[k]=tot+1; dfs(k+1,tot+1); REP(i,1,tot){ bel[k]=i; dfs(k+1,tot); } } int main(){ File(); init(); dfs(1,0); printf("%lld\n",ans); return 0; }