题意:
选择不超过
K
个
(n≤500,K≤500)
题解:
首先,很容易想到状压,压缩当前已经选过某质数的状态。对于质数个数小于
20
可以直接过,但是问题是现在
n
以内的质数个数很多,无法压缩。
考虑状压DP的本质:从前往后依次加入数,并更新之前的所有没有与他共用质因数的状态。显然,对于那些质因数分解后只有小质数的数可以直接状压DP,现在考虑如何加入其他大的数。
设置一个分界点
现在已经枚举到质因数
j
,开始枚举包含
处理完所有
其实这就是个分组背包而已。
代码中先处理了所有不包含
1
的情况,最后对于小于
#include<bits/stdc++.h>
using namespace std;
inline int read(){
char ch=getchar();int i=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();}
return i*f;
}
const int LIM=(1<<8)+20,Maxn=5e2+50;
const int prime[8]={2,3,5,7,11,13,17,19};
const int Mod=1e9+7;
int T,n,k,dp[Maxn][LIM],status[Maxn],lim=(1<<8)-1;
vector<int>v[Maxn];
inline void pre(){
for(register int i=500;i>=2;i--){
int tmp=i,bz=1;
for(register int d=0;d<8&&prime[d]<=tmp;++d){
int cnt=0;
while(!(tmp%prime[d]))tmp/=prime[d],++cnt;
if(cnt>=2){bz=0;break;}
else if(cnt)status[i]^=(1<<d);
}
if(!bz)continue;
if(tmp==1)v[i].push_back(i);
else v[tmp].push_back(i);
}
}
int main(){
pre();
T=read();
while(T--){
n=read();k=read();
memset(dp,0,sizeof(dp));
dp[0][0]=1;
for(register int i=2;i<=n;i++){
for(register int o=k;o>=1;--o)
for(register int j=v[i].size()-1;j>=0;j--){
if(v[i][j]>n)break;
register int sta=status[v[i][j]];
register int R=lim^sta;
for(register int r=R;;r=(r-1)&R){
(dp[o][r|sta]+=dp[o-1][r])%=Mod;
if(!r)break;
}
}
}
int ans=0;
for(register int i=1;i<=k;i++){
for(register int j=0;j<=lim;j++){
int t=dp[i][j];
(ans+=t)%=Mod;
if(i!=k)(ans+=t)%=Mod;
}
}
cout<<ans+1<<endl;
}
}