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;
}
}