Codeforces 908E. New Year and Entity Enumeration

先不管集合T

对于一个good set S,他的性质就是他里面的元素不断AND后仍然在这个集合里,且每个元素的补集都在这个集合里

令f(i)表示集合S中含i的所有元素AND起来的值,f(i)是一组二进制位,且可以发现,若存在 jf(i)(ji) ,则必定有 f(j)=f(i)
简单的证明:
反证法
每个含i的数都含j,那么有 f(j)f(i) ,若存在某个数x满足 jx,ix ,使得 f(j)f(i) ,那么考虑x的补集 y=x XOR M ,一定有 jy,iy ,那么可以得到 jf(i) ,与题设矛盾

那么S就把m个二进制位划分成了若干个块,不同块之间是相互独立的,且同一个块内的所有二进制位在S中的所有数中的状态都相同,可以把他们视为一个二进制位看
因为我们可以用AND和补集操作弄出OR操作
若有k个块,S中就会有 2k 个元素,每一种块的划分唯一对应一个good set S

再考虑集合T,我们要求的是满足 TS 的good set S的个数,也就是满足 TS 的情况下,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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值