【题目】
lydsy
给定
S
S
S个有
n
n
n个节点的图,求有多少个子集的异或为一个连通图。
n
≤
10
,
S
≤
60
n\leq 10,S\leq 60
n≤10,S≤60
【题目】
不会。
首先连通并不好做,考虑求不连通有多少个。那么先花费贝尔数的时间枚举一个划分,表示不同划分里的点在不同的连通块,但同一个划分里的点不一定在同一个连通块。也就是说所有连接两个不同划分的边都必须为 0 0 0,方案数可以通过线性基求自由元个数求出来。
接下来需要对这个东西进行容斥,我们设 f i f_i fi表示至少有 i i i个连通块的方案数, g i g_i gi表示恰好有 i i i个连通块的方案数,那么我们上面求的就是 f f f。
考虑容斥系数要考虑
f
f
f和
g
g
g的关系,我们考虑一幅图如果实际上被分成了
m
m
m个连通块,那么它会被计算多少次:
∑
i
=
1
m
S
(
m
,
i
)
\sum_{i=1}^m S(m,i)
i=1∑mS(m,i)
于是我们有:
f
i
=
∑
j
=
i
n
g
j
⋅
S
(
j
,
i
)
f_i=\sum_{j=i}^n g_j\cdot S(j,i)
fi=j=i∑ngj⋅S(j,i)
然后斯特林反演:
g
i
=
∑
j
=
i
n
(
−
1
)
j
−
i
⋅
s
(
j
,
i
)
⋅
f
j
g_i=\sum_{j=i}^n (-1)^{j-i}\cdot s(j,i)\cdot f_j
gi=j=i∑n(−1)j−i⋅s(j,i)⋅fj
可以得到:
g
1
=
∑
i
=
1
n
(
−
1
)
i
−
1
⋅
(
i
−
1
)
!
⋅
f
i
g_1=\sum_{i=1}^n(-1)^{i-1}\cdot (i-1)!\cdot f_i
g1=i=1∑n(−1)i−1⋅(i−1)!⋅fi
然后就没有了,每次重新计算
f
f
f套柿子即可。
复杂度 O ( B n ⋅ n 3 ) O(B_n\cdot n^3) O(Bn⋅n3)
【参考代码】
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=12,M=65;
int S,n,m,id[N];
char s[N*N];
bool e[M][N][N];
ll ans,fac[N];
namespace Base
{
int cnt;ll A[M];
void insert(ll x)
{
for(int i=S-1;~i;--i) if(x&(1ll<<i))
{
if(!A[i]) {A[i]=x;++cnt;return;}
else x^=A[i];
}
}
ll calc()
{
memset(A,0,sizeof(A));cnt=0;
for(int i=1;i<n;++i) for(int j=i+1;j<=n;++j)
if(id[i]^id[j])
{
ll now=0;
for(int k=0;k<S;++k) now|=(1ll<<k)*e[k][i][j];
insert(now);
}
//for(int i=1;i<=n;++i) printf("%d ",id[i]); puts("");
//printf("%d\n",cnt);
return 1ll<<(S-cnt);
}
}
void dfs(int d,int x)
{
if(d==n+1) {ans+=(x&1?1:-1)*Base::calc()*fac[x-1];return;}
for(int i=1;i<=x;++i) id[d]=i,dfs(d+1,x);
id[d]=++x;dfs(d+1,x);
}
int main()
{
#ifdef Durant_Lee
freopen("BZOJ4671.in","r",stdin);
freopen("BZOJ4671.out","w",stdout);
#endif
scanf("%d",&S);
for(int i=0,m;i<S;++i)
{
scanf("%s",s);m=strlen(s);
n=ceil(sqrt(m*2));
for(int j=1,x=0;j<n;++j) for(int k=j+1;k<=n;++k,++x) e[i][j][k]=s[x]^48;
}
fac[0]=1;
for(int i=1;i<=n;++i) fac[i]=fac[i-1]*i;
dfs(1,0);printf("%lld\n",ans);
return 0;
}