HDU - 6125
从 1-n 选出不超过 k 个数,使得所有数乘积不能被任何平方数整除。
一开始算了一下500以内的质数,还是挺多的,但是注意到
500−−−√
中的素数只有8个,那么就可以做装压dp了,对于含有超过
500−−−√
的素数,进行因式分解,并且把拥有相同超过
500−−−√
的质数放在一起(肯定最多只有一个),然后dp的时候通过调换枚举顺序就可以保证同一组的肯定不会同时更新一个状态,即保证了同一组不会出现到一起。
#include <bits/stdc++.h>
#include <vector>
using namespace std;
const int maxn = 500;
const int mo = 1e9+7;
int p[maxn+10], f[maxn+10], ans, n, m, a[maxn+10][maxn+10], ff[maxn+1];
bool flag[501];
vector<int> G[maxn];
void getPrime() {
memset(p, 0, sizeof(p));
for (int i = 2; i <= maxn; i++) {
if (!p[i]) p[++p[0]] = i;
for (int j = 1; j <= p[0] && p[j] <= maxn/i; j++) {
p[p[j]*i] = 1;
if (i%p[j] == 0) break;
}
}
}
int get(int x) {
if (!flag[x]) return -1;
int t = 0, y = 1;
for (int i = 1; i <= 8; i++) {
if (x%p[i] == 0) t += y;
y *= 2;
}
return t;
}
void init() {
memset(flag, true, sizeof(flag));
for (int i = 1; i <= 500; i++) G[i].clear();
for (int i = 1; i <= p[0]; i++) {
for (int j = 1; j*p[i]*p[i] <= maxn; j++) flag[j*p[i]*p[i]] = false;
}
for (int i = 1; i <= 500; i++) f[i] = get(i);
for (int i = 9; i <= p[0]; i++) {
for (int j = 1; j*p[i] <= maxn; j++) if (flag[j*p[i]]) {
flag[j*p[i]] = false;
G[i-8].push_back(p[i]*j);
}
}
}
int main() {
getPrime();
init();
int T;
for (int i = 0; i <= maxn; i++) ff[i] = f[i];
scanf("%d", &T);
while (T--) {
scanf("%d %d", &n, &m);
memset(a, 0, sizeof(a));
a[0][0] = 1;
for (int k = 1; k <= n; k++) if (flag[k]) {
int t = f[k];
for (int i = 255; i >= 0; i--)
for (int j = m-1; j >= 0; j--) if (a[i][j] && (t&i) == 0) {
a[t|i][j+1] += a[i][j];
a[t|i][j+1] %= mo;
}
}
for (int k = 1; k <= p[0]-8; k++) {
for (int i = 255; i >= 0; i--)
for (int j = m-1; j >= 0; j--) if (a[i][j]) {
for (int x = 0; x < G[k].size(); x++) {
int t = f[G[k][x]];
if ((i&t) == 0 && G[k][x] <= n) {
a[i|t][j+1] += a[i][j];
a[i|t][j+1] %= mo;
}
}
}
}
a[0][0] = 0;
ans = 0;
for (int i = 0; i <= 255; i++)
for (int j = 1; j <= m; j++) ans = (ans+a[i][j])%mo;
printf("%lld\n", ans);
}
}