• Free from square HDU6125


    #include<cstdio>
    #include<cstring>
    #include<cmath>
    using namespace std;
    #define mod 1000000007
    int T,n,k;
    int q[95]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,
    233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499};
    int a[96][96][256],vis[96][96][256];
    int a1[256][9],vis1[256][9];
    int subsol(int sg,int k)//可选取范围在小于根号N范围
    {
        int res;
        if(k>8)k=8;
        if(k<0)return 0;//不再选
        if(sg==0) return 1;//可选范围为0;那么这一支的贡献返回1,如果把这种选择看成一棵树的话;
        if(vis1[sg][k]) return a1[sg][k];
        int a=0;
        while((1<<a)<=sg)a++;a--;
        res=subsol(sg^(1<<a),k);//不取q[a];
        for(int i=0;i<(1<<a);i++)
        if((sg&i)==i)//i组合状态合法
        {
            int s=q[a];
            for(int j=0;j<a;j++)
            {
                if(i&(1<<j))//j是选好的质数
                {
                    s*=q[j];
                    if(s>n)break;
                }
            }
            if(s<=n)(res+=subsol(sg^(1<<a)^i,k-1))%=mod;
        }
        vis1[sg][k]=1;
        return a1[sg][k]=res;
    }
    
    
    
    
    int sol(int lim,int m,int k,int sg)
    {
        int res;
        if(k<0)return 0;//不在取
        if(m<lim) return subsol(sg,k);//大于根号n的质数已经枚举完了
        if(vis[m][k][sg]) return a[m][k][sg];//取第m个且在sg的范围内取K个
        //可以知道这里m都是大于根号n的质数
        res=sol(lim,m-1,k,sg);//不选q[m]的情况
        for(int i=0;i<=255;i++)
        {
            if((sg&i)==i)//如果i在可选区间内,就选
            {
                int s=q[m];//q[m]必选
                for(int j=0;j<=7;j++)//找出选的那些位
                if(i&(1<<j))
                {
                   s*=q[j];
                   if(s>n)break;//这个数选多了。这个组合不成立
                }
                if(s<=n)//这个组合选取成功,找到了一个数,现在找下一个数
                    (res+=sol(lim,m-1,k-1,sg^i))%=mod;//q[m]不能再选了,可选范围也去掉了选过的那些质数
            }
        }
        vis[m][k][sg]=1;//当前值已经找到,省去了重复计算
        return a[m][k][sg]=res;
    }
    
    
    
    int main()
    {
        //freopen("input.txt","r",stdin);
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d",&n,&k);
            memset(vis,0,sizeof vis);
            memset(vis1,0,sizeof vis1);
            if(k>95)k=95;
            double h=sqrt(n)+1e-6;
            int a=0;while(q[a]<=h)a++;
            int b=0;while(q[b]<=n&&b<95)b++;
            int ans=sol(a,b-1,k,(1<<a)-1)+sol(a,b-1,k-1,(1<<a)-1)-1;
            printf("%d
    ",ans%mod);//不包含1+包含1-空
        }
        return 0;
    }

    反向思维,从质因数乘积组成成合法序列入手,可以抽象成一颗乘积的组合树,每一支里面选择的质数互斥;
  • 相关阅读:
    QOMO Linux 4.0 正式版发布
    LinkChecker 8.1 发布,网页链接检查
    pgBadger 2.1 发布,PG 日志分析
    Aletheia 0.1.1 发布,HTTP 调试工具
    Teiid 8.2 Beta1 发布,数据虚拟化系统
    zLogFabric 2.2 发布,集中式日志存储系统
    开源电子工作套件 Arduino Start Kit 登场
    Piwik 1.9 发布,网站访问统计系统
    Ruby 1.9.3p286 发布,安全修复版本
    toBraille 1.1.2 发布,Java 盲文库
  • 原文地址:https://www.cnblogs.com/MeowMeowMeow/p/7375293.html
Copyright © 2020-2023  润新知