平方自由数
Problem
多组数据。
给出一个 \(\{1,2,\cdots , n\}\) 的全集,从其中选取子集 \(S\),满足:子集大小 \(\vert S \vert \le k\),(至少选择一个数) \(\prod_{u\in S}u\) 是一个平方自由数(square-free integer),即这个数的质因子幂指数均为 \(1\)。求满足条件的子集数,对 \(10^9+7\) 取模。
Input
第一行包含一个整数 \(T(T \le 5)\),表示数据组数。
对于每组数据,每行包含两个正整数 \(n, k(n, k \le 500)\)。
Output
对于每组数据,输出一个整数,表示答案。
Sample
Input 1
2
4 2
6 4
Output 1
6
19
Solution
一个平方自由数可以表示成一些质数的乘积,因此可以考虑每一个质因数是否选择。同时发现一个很好的性质,一个 \([1,n]\) 的数最多只有一个质因子 \(\geq \sqrt{n}\) 个。所以对于 \(\sqrt{n}\) 以内的质数(\(8\) 个),可以状压处理。然后枚举大于 \(23\) 的质数,并将它搭配好不超过 \(23\) 的若干个质因子,\(dp\) 即可。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int kmax = 505;
const int kmaxM = 8;
const int Mod = 1e9 + 7;
int t, n, k;
bool b[kmax];
int pr[kmax], pc;
long long fac[1 << kmaxM];
long long f[1 << kmaxM][kmax], g[1 << kmaxM][kmax], res; // 前8个质数是否选择,一共选了多少个数
void Prime() { // 筛质数
b[1] = 1;
for(int i = 2; i < kmax; i++) {
if(!b[i]) {
pr[pc++] = i;
}
for(int j = 0; j < pc && i * pr[j] < kmax; j++) {
b[i * pr[j]] = 1;
if(i % pr[j] == 0) break;
}
}
}
void Mul() {
for(int i = 0; i < 1 << kmaxM; i++) {
fac[i] = 1;
for(int j = 0; j < kmaxM; j++) {
if(i & (1 << j)) {
fac[i] = fac[i] * pr[j]; // 计算乘积
}
}
}
}
void Solve() {
cin >> n >> k;
memset(f, 0, sizeof(f));
memset(g, 0, sizeof(g));
f[0][0] = 1, res = 0;
for(int i = 0; i < 1 << kmaxM; i++) {
if(fac[i] > n) continue; // 取值不能超过n
for(int j = k; j; j--) {
for(int l = ((1 << kmaxM) - 1) ^ i; ; l = (l - 1) & (((1 << kmaxM) - 1) ^ i)) {
f[l | i][j] = (f[l | i][j] + f[l][j - 1]) % Mod; // 转移
if(!l) break;
}
}
}
for(int t = kmaxM; t < pc; t++) { // 枚举大于23的质数
memcpy(g, f, sizeof(f));
for(int i = 0; i < 1 << kmaxM; i++) { // 枚举前8个质数的选择情况
if(fac[i] * pr[t] > n) continue;
for(int j = k; j; j--) {
for(int l = ((1 << kmaxM) - 1) ^ i; ; l = (l - 1) & (((1 << kmaxM) - 1) ^ i)) {
f[l | i][j] = (f[l | i][j] + g[l][j - 1]) % Mod; // 转移
if(!l) break;
}
}
}
}
for(int i = 0; i < 1 << kmaxM; i++) {
for(int j = 1; j <= k; j++) {
res = (res + f[i][j]) % Mod; // 统计结果
}
}
cout << res << '\n';
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
Prime();
Mul();
cin >> t;
while(t--) { // 多组数据
Solve();
}
return 0;
}