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;
}
posted @ 2021-01-21 11:00  Martin_MHT  阅读(67)  评论(0)    收藏  举报