【NOIP2017提高A组冲刺11.9】乘积

Description

豆豆最近在潜心研究数学,他发现了一类很有趣的数字,叫做无平方因子数。也就是这一类数字不能够被任意一个质数的平方整除,比如6、7、10都是无平方因子数,而12则不是。
所以豆豆在思考一个问题——选择不超过K个N以内的正整数乘起来,使得乘积是一个无平方因子数,有多少种取法?(每个数只能取一次)

Input

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

Output

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

Sample Input

3
1 1
6 4
4 2

Sample Output

1
19
6

Data Constraint

对于10%的数据:N≤8;
对于40%的数据:N≤16;
对于70%的数据:N≤30;
对于100%的数据:1≤T≤5;1≤K≤N≤500。

思路

70%
方法一:dfs。先筛出质数,然后分解质因数。设f[i]为恰好选i个的方案(不包括1)。搜索得出f。ans=sigma(f(n-1))*2+f[0]+f[k];

方法二:统计 N 以内的质数有多少个,状态压缩到一个数 mask 当中,DP[i][k][mask]
表示前 i 个数选择了 k 个数,现在含有 mask 中这些因数。
复杂度 O(2^10*N)

100%

状压 DP+分组背包。
每一个数只会含有一个比 sqrt(N)大的质因数,所以我们把所有数字按照含有
比 sqrt(n)大的数分组,
sqrt(n)以内的质数只有 8 个。
然后 DP[i][j][mask]表示计算完了前 i 组数,选择了 j 个数字,mask 表示当
前乘积含有比 sqrt(n)小的质因数的集合。
然后进行背包就好了。

100%代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<memory.h>
#define maxn 507
using namespace std;
const int zs[8]={2,3,5,7,11,13,17,19};
int a[maxn],tt,n,k,f[maxn][1<<8],b[maxn][maxn];
int main()
{
    freopen("mul.in","r",stdin);
    freopen("mul.out","w",stdout);
    for(int i=1; i<=500; i++)
    {
        int t=i;
        for(int j=0; j<8; j++)
        {
            if(t%zs[j]==0) a[i]+=1<<j,t/=zs[j];
            if(t%zs[j]==0)
            {
                a[i]=-1; break;
            }
        }
    }
    scanf("%d",&tt);
    while(tt--)
    {
        memset(b,0,sizeof(b)); memset(f,0,sizeof(f));
        scanf("%d%d",&n,&k);
        for(int i=1; i<=n; i++)
        {
            if(a[i]==-1) continue;
            int t=i;
            for(int j=0; j<8; j++) if(t%zs[j]==0) t/=zs[j];
            if(t!=1) b[t][++b[t][0]]=i; else b[i][++b[i][0]]=i;
        }
        f[0][0]=1;
        for(int i=1; i<=n; i++) if(b[i][0])
            for(int j=k-1; j>=0; j--) for(int l=1; l<=b[i][0]; l++)
                for(int zy=0,v=a[b[i][l]]; zy<(1<<8); zy++)
                    if(!(zy&v)) f[j+1][zy+v]=(f[j+1][zy+v]+f[j][zy])%1000000007;
        long long ans=0;
        for(int i=1; i<=k; i++) 
            for(int zy=0; zy<(1<<8); zy++) 
                ans=(ans+f[i][zy])%1000000007;
        cout<<ans<<endl;
    }
}

猜你喜欢

转载自blog.csdn.net/eric1561759334/article/details/80030257