先不管集合T
对于一个good set S,他的性质就是他里面的元素不断AND后仍然在这个集合里,且每个元素的补集都在这个集合里
令f(i)表示集合S中含i的所有元素AND起来的值,f(i)是一组二进制位,且可以发现,若存在
j∈f(i)(j≠i)
,则必定有
f(j)=f(i)
简单的证明:
反证法
每个含i的数都含j,那么有
f(j)⊆f(i)
,若存在某个数x满足
j∈x,i∉x
,使得
f(j)⊂f(i)
,那么考虑x的补集
y=x XOR M
,一定有
j∉y,i∈y
,那么可以得到
j∉f(i)
,与题设矛盾
那么S就把m个二进制位划分成了若干个块,不同块之间是相互独立的,且同一个块内的所有二进制位在S中的所有数中的状态都相同,可以把他们视为一个二进制位看
因为我们可以用AND和补集操作弄出OR操作
若有k个块,S中就会有
2k
个元素,每一种块的划分唯一对应一个good set S
再考虑集合T,我们要求的是满足
T⊆S
的good set S的个数,也就是满足
T⊆S
的情况下,S里面元素的集合划分的方案数
T的作用其实就是给出了一个划分,我们对T做这个f(i),此时不在一个块内的位在S中也一定不会在同一个块中,我们做完T的集合划分后,假设把这m个位划分成了k个块,第i个块有a[i]个二进制位,因为不同块之间互相独立,最后的方案数就是
ans=∏ki=1bell(ai)
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn = 1100;
const int mod = 1e9+7;
inline void add(int &a,const int &b){a+=b;if(a>=mod)a-=mod;}
int n,m;
int C[maxn][maxn];
int f[maxn];
bitset<maxn>a[maxn],base[maxn],M;
bool v[maxn];
char str[maxn];
int main()
{
C[0][0]=1;
for(int i=1;i<maxn;i++)
{
C[i][0]=1;
for(int j=1;j<=i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
}
f[0]=1;
for(int i=1;i<maxn;i++)
{
for(int j=0;j<i;j++)
add(f[i],(ll)C[i-1][j]*f[i-1-j]%mod);
}
scanf("%d%d",&m,&n); M.set();
for(int i=1;i<=n;i++)
{
scanf("%s",str+1);
for(int j=1;j<=m;j++)
if(str[j]=='1') a[i][j]=1;
}
for(int i=1;i<=m;i++)
{
base[i]=M;
for(int j=1;j<=n;j++)
{
if(a[j][i]) base[i]&=a[j];
else base[i]&=(a[j]^M);
}
}
/*for(int i=1;i<=m;i++)
{
for(int j=1;j<=m;j++) printf("%d",base[i][j]?1:0);
puts("");
}*/
int re=1;
for(int i=1;i<=m;i++)
{
int num=0;
for(int j=i;j<=m;j++) if(base[i][j]&&!v[j])
num++,v[j]=true;
re=(ll)re*f[num]%mod;
}
printf("%d\n",re);
return 0;
}