zoukankan      html  css  js  c++  java
  • 安徽师大附中%你赛day7 T2 乘积 解题报告

    乘积

    题目背景

    (mathrm{Smart}) 最近在潜心研究数学, 他发现了一类很有趣的数字, 叫做无平方因子数。 也就是这一类数字不能够被任意一个质数的平方整除, 比如(6)(7)(10)都是无平方因子数, 而(12)则不是。

    题目描述

    所以 (mathrm{Smart}) 在思考一个问题——选择不超过 (K)(N) 以内的正整数乘起来, 使得乘积是一个无平方因子数, 有多少种取法? (每个数只能取一次)

    输入输出格式

    输入格式

    第一行一个整数 (T) 表示数据组数。
    接下来 (T) 行, 每行两个整数 (N),(K), 意思如题面所述。

    输出格式

    对于每一组数据, 输出一个整数表示取法的方案数对 (10^9+7) 取模后的数值。

    说明

    (10\%)的数据: (N≤8)

    (40\%)的数据: (N≤16)

    (70\%)的数据: (N≤30)

    (100\%)的数据: (1≤T≤5)(1≤K≤N≤500)


    70pts 有非常多种搞法,然而状压是最难写的但是最可能继续玩出正解的。。

    可是我太菜,比赛时写了个麻烦的状压

    (dp[i][j][s])代表前(i)个数选择了(j)个素数状态为(s)的方案数

    Code:

    #include <cstdio>
    #include <cstring>
    const int N=502;
    int num[30][10],dat[30];
    int pri[N],is[N],v[N],cnt,tot;
    int div[N][100];
    void init()
    {
        for(int i=2;i<=500;i++)
        {
            if(!is[i])
            {
                pri[++cnt]=i;
                v[i]=i;
            }
            for(int j=1;j<=cnt&&i*pri[j]<=500;j++)
            {
                if(v[i]<pri[j]) break;
                v[i*pri[j]]=pri[j];
                is[i*pri[j]]=1;
            }
        }
        for(int i=2;i<=500;i++)
        {
            int t=i;
            for(int j=1;j<=cnt;j++)
                while(t%pri[j]==0)
                    div[i][j]++,t/=pri[j];
        }
        for(int i=2;i<=33;i++)
        {
            int flag=1;
            for(int j=1;j<=12;j++)
                if(div[i][j]>1) {flag=0;break;}
            if(!flag) continue;
            ++tot;
            dat[tot]=i;
            for(int j=1;j<=12;j++)
                if(div[i][j])
                    num[tot][j]=1;
        }
    }
    int mod=1e9+7;
    int n0,k;
    int dp[20][20][1200];
    void work()
    {
        scanf("%d%d",&n0,&k);
        int l=0,n=0;
        for(int i=1;;i++)//素数长度上界
        {
            if(pri[i]<=n0) ++l;
            else break;
        }
        for(int i=1;;i++)//选数个数上界
        {
            if(dat[i]<=n0) ++n;
            else break;
        }
        k=(k<=n?k:n);
        memset(dp,0,sizeof(dp));
        for(int i=0;i<=n;i++) dp[i][0][0]=1;
        int ans=0;
        for(int i=1;i<=n;i++)//前i个数
            for(int j=1;j<=k;j++)//取了j个
                for(int s=1;s<1<<l;s++)//素数集合状态
                {
                    int las=s,flag=1;
                    dp[i][j][s]=dp[i-1][j][s];
                    for(int q=1;q<=l;q++)
                        if(num[i][q])//如果这一位是1
                        {
                            if((s>>q-1)&1) las^=1<<q-1;//变成0
                            else {flag=0;break;}
                        }
                    if(flag) (dp[i][j][s]+=dp[i-1][j-1][las])%=mod;
                }
        for(int i=1;i<=k;i++)
            for(int s=1;s<1<<l;s++)
            {
                if(i!=k) (ans+=dp[n][i][s]<<1)%=mod;
                else (ans+=dp[n][i][s])%=mod;
            }
        printf("%d
    ",ans+1);
    }
    int main()
    {
        freopen("mult.in","r",stdin);
        freopen("mult.out","w",stdout);
        init();
        int t;
        scanf("%d",&t);
        while(t--)
            work();
        return 0;
    }
    

    而正解只是运用分组背包的思想

    注意到大于19的质数只可能出现一个

    那我们实际上就只需要找到状压2,3,5,7,11,13,17,19这几个素数就行了

    其他大于19的素数按照这个素数进行分组,剩下的同时按前几个素数做就可以了

    很巧妙的思想,然而数据不好造,答案一样的期望炒鸡高

    Code:

    #include <cstdio>
    #include <cstring>
    #include <vector>
    #define ll long long
    const int N=500;
    const ll mod=1e9+7;
    const int pri[9]={0,2,3,5,7,11,13,17,19};
    using namespace std;
    vector <int > g[N+10];
    int belong[N+10],sta[N+10],n,k;
    void init()
    {
        memset(belong,0,sizeof(belong));
        memset(sta,0,sizeof(sta));
        for(int i=1;i<=N;i++) g[i].clear();
        for(int i=1;i<=n;i++)
        {
            belong[i]=i;
            for(int j=1;j<=8;j++)
            {
                if(i%(pri[j]*pri[j])==0) {sta[i]=-1;break;}
                else if(i%pri[j]==0) {belong[i]/=pri[j];sta[i]|=1<<j-1;}
            }
        }
        for(int i=1;i<=n;i++)
        {
            if(~sta[i])
            {
                if(belong[i]!=1)
                    g[belong[i]].push_back(sta[i]);
                else
                    g[i].push_back(sta[i]);
            }
        }
    }
    ll dp[N+10][256];
    void work()
    {
        scanf("%d%d",&n,&k);
        init();
        memset(dp,0,sizeof(dp));
        dp[0][0]=1;
        for(int i=1;i<=n;i++)
        {
            for(int j=k;j;j--)
                for(int s=255;~s;s--)
                {
                    if(!dp[j][s]) continue;
                    for(int l=0;l<g[i].size();l++)
                    {
                        int now=g[i][l];
                        if(now&s) continue;
                        (dp[j][now|s]+=dp[j-1][s])%=mod;
                    }
                }    
        }
        ll ans=0;
        for(int i=1;i<=k;i++)
            for(int s=0;s<=255;s++)
                (ans+=dp[i][s])%=mod;
        printf("%lld
    ",ans);
    }
    int main()
    {
        //freopen("mult.in","r",stdin);
        //freopen("mult.out","w",stdout);
        int t;scanf("%d",&t);
        while(t--)
            work();
        return 0;
    }
    
    

    2018.8.20

  • 相关阅读:
    Windows 环境下配置 git bash 的 HOME 默认路径
    SQL Server 2008 r2 输入SQL语句不能自动提示的解决办法
    搭建调用 WebService 的 ASP.NET 网站 (VS2010, C#)
    【问题解决】线程间操作无效:从不是创建控件“textBox1”的线程访问它
    使用 hexdump dump 文件内容
    调用 WebService 浏览器提示 500 (Internal Server Error) 的原因及解决办法
    PHP的文件格式应该以UTF-8无BOM编码
    继电器电路
    ROS零门槛教程系列(二)——Linux常用指令:mkdir、tar、 unzip、cp、scp、mv、rm、find、apt、ssh
    ROS零门槛教程系列(一)——ubuntu安装
  • 原文地址:https://www.cnblogs.com/butterflydew/p/9507033.html
Copyright © 2011-2022 走看看