这道题思维很奇特。。。
题目大意:从给出的n个数中选出至少一个,至多k个,保证这些数的乘积没有一个因数
是完全平方数。也就是说没有一个质因数出现超过1次。
n和k小于等于500。那么因为根号500以内的质因数只有8个,很容易想到状压,
把每个数的质因数按照是否出现压成一个二进制数,如果出现多次,这个数就不可用了
之后枚举每个数,枚举每个状态,可以从当前状态向下转移当且仅当当前状态和枚举的
数字没有重复的质因数,最后求出状态的和。
高高兴兴的写了
Wrong Answer
好了,什么都没看到,很显然当同时出现23和46时这样根号500以上的质因数和他的
倍数时,这么枚举是会多情况的,但是又不能压缩所有的质因数,怎么办?
这是就可以分组背包了,把前8个质因数分出来计算,其余的按每个质因数和他的倍数
压在一起,计算时先用与后面质因数无关的数(也就是至于前八个质因数有关的
,在第一个背包里),他们可以压在一起计算,不用每个数都自己一个包(因为要
保证包内部数字不能同时出现)。
这样无需判断23和46这种问题,因为他们在同一个包里,不能互相取。
之后按上面的方法枚举就好了,注意空间开不下,第一位要用滚动数组。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define mode 1000000007
using namespace std;
typedef long long ll;
int n,k;
int pri[105];
bool used[505];
int unc[505];
int cnt = 0;
ll f[2][(1<<8)+1][505];
int bag[105][505];
int cct[105];
int sit[505];
void gett()
{
for(int i = 2;i <= 500;i++)
{
if(!used[i])pri[cnt++] = i;
for(int j = 0;j < cnt&&i*pri[j] <= 500;j++)
{
used[i*pri[j]] = 1;
if(!(i%pri[j]))break;
}
}
}
void gba()
{
bag[1][++cct[1]] = 1;
for(int i = 2;i <= 500;i++)
{
bool flag = 0;
for(int j = 8;j < cnt&&i >= pri[j];j++)
{
if(!(i%pri[j]))
{
flag = 1;
bag[j-6][++cct[j-6]] = i;
break;
}
}
if(!flag)
{
bag[1][++cct[1]] = i;
}
}
}
void gej()
{
sit[1] = 0;
for(int i = 2;i <= 500;i++)
{
int ty = i;
for(int j = 0;j < 8;j++)
{
int ww = 0;
while(ty%pri[j] == 0)
{
ww++;
ty /= pri[j];
}
if(ww >= 2)
{
unc[i] = 1;
break;
}else if(ww == 1)
{
sit[i] |= (1<<j);
}
}
}
}
int main()
{
int T;
scanf("%d", &T);
gett();
gba();
gej();
while(T--)
{
scanf("%d%d",&n, &k);
memset(f,0,sizeof(f));
f[0][0][0] = 1;
int now = 1,past = 0;
for(int i = 1;i <= cct[1];i++)
{
memset(f[now],0,sizeof(f[now]));
for(int j = 0;j <= (1<<8)-1;j++)
{
for(int t = 0;t <= k;t++)
{
f[now][j][t] = f[past][j][t];
}
}
for(int j = 0;j <= (1<<8)-1;j++)
{
for(int t = 0;t < k;t++)
{
if(!(sit[bag[1][i]]&j)&&!unc[bag[1][i]]&&bag[1][i]<=n)
{
f[now][j|sit[bag[1][i]]][t+1] += f[past][j][t];
f[now][j|sit[bag[1][i]]][t+1] %= mode;
}
}
}
swap(now,past);
}
for(int i = 2;i < cnt-6;i++)
{
memset(f[now],0,sizeof(f[now]));
for(int j = 0;j <= (1<<8)-1;j++)
{
for(int t = 0;t <= k;t++)
{
f[now][j][t] = f[past][j][t];
}
}
for(int u = 1;u <= cct[i];u++)
{
for(int j = 0;j <= (1<<8)-1;j++)
{
for(int t = 0;t < k;t++)
{
if(!(sit[bag[i][u]]&j)&&!unc[bag[i][u]]&&bag[i][u]<=n)
{
f[now][j|sit[bag[i][u]]][t+1] += f[past][j][t];
f[now][j|sit[bag[i][u]]][t+1] %= mode;
}
}
}
}
swap(now,past);
}
ll ans = 0;
for(int i = 0;i <= (1<<8)-1;i++)
{
for(int j = 1;j <= k;j++)
{
ans += f[past][i][j];
ans %= mode;
}
}
printf("%I64d\n",ans%mode);
}
return 0;
}