JZOJ5464. 乘积
题目大意
选择不超过\(k\)个\(n\)以内的正整数乘起来, 使得乘积是一个无平方因子数, 求方案数. (不能不选)
\(n, k <= 500\)
解题思路
首先自己就是有平方因子数的数直接排除.
筛出\(500\)以内的质数, 得到最多有\(95\)个质数在最后的乘积中. 所以\(k\)最大是\(96\).
这题的思路很套路, 所以需要总结一下 : 那就是, 分质数的大小讨论.
具体地, 小于等于\(\sqrt{n}\)的质数最多有\(8\)个, 因为它们的乘积可能不会超过\(n\), 所以一个小于等于\(n\)的数中可能包含多个这样的因子, 因此我们需要状压状态.
但是含有大于\(\sqrt{n}\)的质因子的数就不一样了呀! 这些数根据最大质因子分类以后, 显然是不会互相影响的.(认真思考这句话!)
于是每一类选取一个, 设\(f_{i,j,S}\)表示做到第\(i\)个质数, 选了\(j\)个数, 前8个质数选择的状态为\(S\), 此时的方案数. 背包转移显然.
A Trick:枚举\(\text{_s}\)的子集(不能枚举到子集为空的情况):
for(int s = _s; s; s = _s & (s - 1))
#include <cstdio>
#include <vector>
#include <cstring>
#define W 8
#define N 500
#define ll long long
#define MOD 1000000007
#define init(a, b) memset(a, b, sizeof(a))
#define fo(i, a, b) for(int i = (a); i <= (b); ++i)
#define fd(i, a, b) for(int i = (a); i >= (b); --i)
using namespace std;
inline int read() // notice : 1. long long ? 2. negative ?
{
int x = 0; char ch = getchar();
while(ch < '0' || ch > '9') ch = getchar();
while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
return x;
}
int n, k, c, p[100], p2[W + 5] = {1}, mnp[N + 5], mxp[N + 5], st[N + 5];
bool no[N + 5];
vector<int> v[100];
ll f[2][100][(1 << W) + 10]; // f[i][j][S] : the i_th prime , have chosen j, status = S
inline void upd(ll &x, ll y){x += y; x >= MOD && (x -= MOD);}
int main()
{
freopen("mul.in", "r", stdin);
freopen("mul.out", "w", stdout);
fo(i, 1, W) p2[i] = p2[i - 1] << 1;
fo(i, 2, N)
{
if(!mnp[i]) p[++c] = mnp[i] = i, mxp[i] = c;
fo(j, 1, c)
{
if(p[j] * i > N) break ;
mnp[i * p[j]] = p[j], mxp[i * p[j]] = mxp[i];
if(!(i % p[j]))
{
no[i * p[j]] = 1;
break ;
}
no[i * p[j]] = no[i];
}
if(!no[i]) v[mxp[i]].push_back(i);
}
fo(i, 2, N) if(!no[i])
fo(j, 1, W) if(!(i % p[j])) st[i] += p2[j - 1];
// fo(i, 2, N) if(!no[i]) printf("%d %d\n", i, st[i]);
for(int T = read(); T; --T)
{
n = read(), k = read(); k = min(k, 96);
init(f, 0); ll ans = 0;
f[0][1][0] = f[0][0][0] = 1;
int m = 0; for(; p[m] <= n && m <= c; ++m); --m;
int full = (1 << min(8, m)) - 1;
fo(i, 1, m)
fo(j, 0, k)
{
fo(s, 0, full) f[i & 1][j][s] = f[(i & 1) ^ 1][j][s];
if(j)
for(int w = 0, siz = v[i].size(); w < siz && v[i][w] <= n; ++w)
{
int _s = full ^ st[v[i][w]];
for(int s = _s; s; s = _s & (s - 1))
upd(f[i & 1][j][s | st[v[i][w]]], f[(i & 1) ^ 1][j - 1][s]);
upd(f[i & 1][j][st[v[i][w]]], f[(i & 1) ^ 1][j - 1][0]);
}
}
fo(i, 1, k) fo(s, 0, full) upd(ans, f[m & 1][i][s]);
printf("%lld\n", ans);
}
return 0;
}
浙公网安备 33010602011771号