BZOJ4671 异或图
Description
定义两个结点数相同的图 $G_1$ 与图 $G_2$ 的异或为一个新的图 $G$, 其中如果 $(u,v)$ 在 $G_1$ 与$G_2$ 中的出现次数之和为 $1$, 那么边 $(u,v)$ 在 $G$ 中, 否则这条边不在 $G$ 中.
现在给定 $s$ 个结点数相同的图 $G_{1 \cdots s}$, 设 $S={G_1,G_2,\cdots,G_s}$, 请问 $S$ 有多少个子集的异或为一个连通图?
Solution
考虑类似容斥的做法,计算图不连通的情况数
设$f_i$表示将图分为$i$个部分,这$i$个部分之间没有连边,每个部分内部不做要求的方案数,$g_i$表示图分为恰好$i$个连通块的方案数
那么有
$$f_i=\sum _{j=i}^n \begin{Bmatrix}j \\i \end{Bmatrix}g_i$$
斯特林反演:
$$\displaystyle f(n)=\sum_{k=0}^n \begin{Bmatrix}n\\k \end{Bmatrix}g(k)\Longleftrightarrow g(n)=\sum_{k=0}^n(-1)^{n-k}\begin {bmatrix} n\\k \end{bmatrix}f(k)$$
可得(虽然是向上枚举但是应该也对)
$$g_i=\sum _{j=i}^n (-1)^{j-i}\begin{bmatrix}j \\i \end{bmatrix}f_j$$
题中要求的是$g_1=\sum _{i=1}^n (-1)^{i-1}(i-1)!f_i$
问题转化为求$f_i$
可以想到对于确定的分组,每个部分之间的边在一个图中存在状态是确定的
所以可以用线性基,向其中加入01串,每个串表示对应的图中部分之间连边是否存在
那么就是求这些01串可以异或出0的方案数,设线性基中数的个数为$c$
答案为$2^{s-c}$
在$Bell(n)$复杂度内搜索即可
在将数加入线性基时发现的:将一个01矩阵每一行看作一个数和每一列看作一个数加入线性基,线性基中数的个数相等
最后的解释是转置矩阵的秩等于原矩阵的秩
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> using namespace std; int s,len,n,tot,g[15]; long long fac[15]={1},base[70],temp,ans,map[65][15][15],b[65]; char str[105]; inline int read() { int f=1,w=0; char ch=0; while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();} while(ch>='0'&&ch<='9') w=(w<<1)+(w<<3)+ch-'0',ch=getchar(); return f*w; } void dfs(int k,int top) { if(k==n) { memset(base,0,sizeof(base)); for(int i=1;i<=s;i++) { temp=0; int cnt=0; for(int j=1;j<=n;j++) for(int l=j+1;l<=n;l++) if(g[j]!=g[l]) temp|=map[i][j][l]<<cnt,++cnt; for(int j=50;~j;j--) if(temp>>j&1) if(base[j]) temp^=base[j]; else { base[j]=temp;break; } } int cnt=0; for(int l=0;l<50;l++) cnt+=(base[l]>0); if(top&1) ans+=(1ll<<s>>cnt)*fac[top-1]; else ans-=(1ll<<s>>cnt)*fac[top-1]; return; } for(int i=1;i<=top+1;i++) g[k+1]=i,dfs(k+1,max(top,g[k+1])); } int main() { s=read(); for(int i=1;i<=s;i++) { scanf("%s",str),len=strlen(str),n=(1+sqrt(1+8*len))/2,tot=0; for(int j=1;j<=n;j++) for(int k=j+1;k<=n;k++) map[i][j][k]=str[tot++]-'0'; } for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i; dfs(0,0); printf("%lld\n",ans); return 0; }