zoukankan      html  css  js  c++  java
  • TOJ4505: KOSARE

    TOJ4505: KOSARE 分享至QQ空间

    Time Limit(Common/Java):10000MS/30000MS     Memory Limit:65536KByte
    Total Submit: 11            Accepted:3

    Description

     

    Mirko found N boxes with various forgotten toys at his attic. There are M different toys, numbered 1 through M, but each of those can appear multiple times across various boxes.
    Mirko decided that he will choose some boxes in a way that there is at least one toy of each kind present, and throw the rest of the boxes away.
    Determine the number of ways in which Mirko can do this.

    Input

     

    The first line of input contains two integers N and M (1 ≤ N ≤ 1 000 000, 1 ≤ M ≤ 20).
    Each of the following N lines contains an integer Ki (0 ≤ Ki ≤ M) followed by Ki distinct integers from interval [1, M], representing the toys in that box.

    Output

     

    The first and only line of output should contain the requested number of ways modulo 1 000 000 007.

    Sample Input

    3 3
    3 1 2 3
    3 1 2 3
    3 1 2 3

    Sample Output

     7

    Source

    COCI 2011/2012 Contest #6

    你有n个盒子,然后有m种物品。每个盒子各有K个物品,你有多少种方式拿盒子,把东西都拿完

    那天我的思路就是去统计这些盒子的状态然后去容斥,显然是爆炸的

    这个要用到那个集合的那个知识,n个元素的他的子集有2^n个,也就是幂集,在这个基础上枚举子集容斥就可以省掉很多步骤

    来看一下这个状态压缩的道理吧 

    之前做过求两个数字有相同数位的对数,那个把每个数直接变为2^digit,找到每一位的hash值,然后把每个数都减1,这样1对应1,2对应2,20对应2^19,这些数加起来是2^20-1,所以数组开到1<<20即可

    每个盒子所对应的hash值知道了,我要和另一个盒子的东西看他们一样么,可以这样想,和他肯定不同的会是谁呢,就是从L开始到枚举的那个中间值(L+R)>>1,mi+i-L肯定是和i是相关的,要进行加法计算,但是我有些子集是重复的,需要枚举重新减去

    #include<stdio.h>
    typedef __int64 ll;
    const ll MD=1e9+7;
    ll a[2000000];
    ll po(int n)
    {
        ll ans=1,b=2;
        while(n)
        {
            if(n&1)ans=ans*b%MD;
            b=b*b%MD;
            n>>=1;
        }
        return ans;
    }
    void la(int L,int R)
    {
        if(L+1==R)
        {
            a[L]=po(a[L]);
            return;
        }
        int mi=(L+R)>>1;
        for(int i=L; i<mi; i++)
            a[mi+i-L]+=a[i];
        la(L,mi);
        la(mi,R);
        for(int i=L; i<mi; i++)
        {
            a[mi+i-L]-=a[i];
            if(a[mi+i-L]<0)a[mi+i-L]+=MD;
        }
    }
    int main()
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=0,k; i<n; i++)
        {
            scanf("%d",&k);
            int f=0;
            for(int j=0,x; j<k; j++)
            {
                scanf("%d",&x);
                f|=(1<<(--x));
            }
            a[f]++;
        }
        la(0,1<<m);
        printf("%I64d",a[(1<<m)-1]);
    }

    这个怎么省去加这个状态呢,就是提前进行一次二进制枚举

    #include<stdio.h>
    typedef __int64 ll;
    const ll MD=1e9+7;
    ll a[2000000];
    ll po(int n)
    {
        ll ans=1,b=2;
        while(n)
        {
            if(n&1)ans=ans*b%MD;
            b=b*b%MD;
            n>>=1;
        }
        return ans;
    }
    void la(int L,int R)
    {
        if(L+1==R)
        {
            a[L]=po(a[L]);
            return;
        }
        int mi=(L+R)>>1;
        la(L,mi);
        la(mi,R);
        for(int i=L; i<mi; i++)
        {
            a[mi+i-L]-=a[i];
            if(a[mi+i-L]<0)a[mi+i-L]+=MD;
        }
    }
    int main()
    {
        int n,m;
        scanf("%d%d",&n,&m);
        int ff=1<<m,f;
        for(int i=0,k; i<n; i++)
        {
            scanf("%d",&k);
            f=0;
            for(int j=0,x; j<k; j++)
            {
                scanf("%d",&x);
                f|=(1<<(--x));
            }
            a[f]++;
        }
        for(int i=0; i<m; i++)
            for(int s=0; s<ff; s++)
                if(s&(1<<i))
                    a[s]+=a[s^(1<<i)];
        la(0,ff);
        printf("%I64d",a[ff-1]);
    }

    直接位运算去统计,这个算法速度最快

    #include<stdio.h>
    const int N=1<<20,MD=1e9+7;
    int cnt[N],B[N],n,m,ans[N],f;
    int main()
    {
        scanf("%d%d",&n,&m);
        f=1<<m;
        B[0]=1;
        for(int i=1; i<=n; i++)
            B[i]=(B[i-1]<<1)%MD;
        for(int i=0,t,x,s; i<n; i++)
        {
            scanf("%d",&t);
            s=0;
            while(t--)
                scanf("%d",&x),s|=1<<x-1;
            cnt[s]++;
        }
        for(int i=0; i<m; i++)
            for(int s=0; s<f; s++)
                if(s&1<<i)
                    cnt[s]+=cnt[s^1<<i];
        for(int s=0; s<f; s++)
            ans[s]=B[cnt[s]];
        for(int i=0; i<m; i++)
            for(int s=0; s<f; s++)
                if(s&1<<i)
                    ans[s]=(ans[s]-ans[s^1<<i]+MD)%MD;
        printf("%d",ans[f-1]);
        return 0;
    }

     

     

     

  • 相关阅读:
    Python 判断所有文件或文件夹
    Ubuntu显卡GPU 驱动的安装
    新系统ubuntu 需要配置项目 安装 cudnn 修改pip国内源
    Ubuntu下设置DNS的方法 和国内公共DNS汇总
    学习vue ,环境搭建(VS code、node.js、cnpm、vue-cli)创建项目 并引入element
    mybatis mysql count(*) 返回结果为null的解决
    使用V2Ry和V2RyN搭建本地代理服务器供局域网用户连接
    类似samba raidrive
    shell if判断语句 报错:syntax error near unexpected token `elif'
    stat()返回失败Invalid parameter
  • 原文地址:https://www.cnblogs.com/BobHuang/p/8059974.html
Copyright © 2011-2022 走看看