bzoj4671 异或图(斯特林反演,线性基)
bzoj4671 异或图(斯特林反演,线性基)
祭奠天国的bzoj。
题解时间
首先考虑类似于容斥的东西。
设 $ f_{ i } $ 为至少有 $ i $ 个连通块的方案数, $ g_{ i } $ 为正好有 $ i $ 个连通块的方案数。
那么有
\[f_{ m } = \sum\limits_{ i = m }^{n} \begin{Bmatrix} i \\ m \end{Bmatrix} g_{ i }
\]
斯特林反演就有
\[g_{ 1 } = \sum\limits_{ i = 1 }^{ n } (-1)^{ i - 1 } \begin{bmatrix} i \\ 1 \end{bmatrix} f_{ i }
\]
其中
\[\begin{bmatrix} i \\ 1 \end{bmatrix} = ( i - 1 )!
\]
那么考虑求 $ f_{ i } $ 。
枚举所有可能的子集划分,复杂度为 $ Bell(n) $ ,
对于每个划分,要保证划分之间的边全部不存在,
由此得出异或方程组,设秩为 $ c $ ,则对答案贡献为 $ 2^{ s - c } $ 。
线性基解决。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long lint;
struct pat{int x,y;pat(int x=0,int y=0):x(x),y(y){}bool operator<(const pat &p)const{return x==p.x?y<p.y:x<p.x;}};
template<typename TP>inline void read(TP &tar)
{
TP ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){ret=ret*10+(ch-'0');ch=getchar();}
tar=ret*f;
}
template<typename TP,typename... Args>inline void read(TP& t,Args&... args){read(t),read(args...);}
namespace RKK
{
const int N=12,M=62;
int n,m,len;lint ans;
char str[114];
bool mp[M][N][N];
lint fac[N];
int bl[N];
lint b[M];
void dfs(int x,int cnt)
{
if(x>n)
{
int s=0;
for(int i=1;i<=n;i++)for(int j=i+1;j<=n;j++)if(bl[i]!=bl[j])
{
lint val=0;
for(int k=1;k<=m;k++)if(mp[k][i][j]) val|=(1ll<<(k-1));
for(int k=1;k<=s;k++) if((val^b[k])<val) val^=b[k];
if(val) b[++s]=val;
}
ans+=fac[cnt]*(1ll<<(m-s));
return;
}
for(int i=1;i<=cnt+1;i++)
bl[x]=i,dfs(x+1,max(cnt,i));
}
int main()
{
read(m);for(int i=1;i<=m;i++)
{
scanf("%s",str+1);if(i==1){len=strlen(str+1);while(n*(n-1)/2!=len) n++;}
for(int j=1,o=0;j<=n;j++)for(int k=j+1;k<=n;k++) mp[i][j][k]=str[++o]-'0';
}
fac[1]=1;for(int i=2;i<=n;i++) fac[i]=fac[i-1]*(1-i);
dfs(1,0),printf("%lld\n",ans);
return 0;
}
}
int main(){return RKK::main();}