省选模拟赛 题解 T1 质数拆分 (背包DP预处理+组合数)

在这里插入图片描述
S 2000000 , n 1 0 18 , q 1 0 5 S\le 2000000,n \le 10^{18},q\le10^5

题解

显然可以发现,只有 S S 为无平方因子数才可能有解。假设 S = i = 1 m p i S=\prod_{i=1}^mp_i p i p_i 均为质数。那么 m 8 m\le8 (前 8 8 小的质数乘起来一定大于 S S )。那么问题就是把 n n 拆成 p i c i \sum p_i\cdot c_i 有多少种方案。 c i c_i 表示 i i 种质数取了多少次。

首先 c i 1 c_i\ge1 ,所以可以先把 n n 减去 p i \sum p_i ,如果为负就肯定无解。

然后发现 p i p_i 肯定为 S S 的约数,那么每个 p i c i p_i\cdot c_i 都可以写成$X\cdot S+Y\cdot a_i的形式。

假设 a i = c i / ( S / p i ) a_i=c_i/(S/p_i) b i = c i m o d ( S / p i ) b_i=c_imod (S/p_i)

i = 1 m p i b i = n M S \sum_{i=1}^mp_i\cdot b_i=n-M\cdot S ,其中 M = i = 1 m a i M=\sum_{i=1}^m{a_i}

知道 b i < S / p i b_i<S/p_i ,那么用完全背包预处理左边的部分。
然后 M M 的拆分直接组合数插板法就行了。

注意完全背包为了保证 b i b_i 的范围要减去 b i S / p i b_i\ge S/p_i ,即 b i p i S b_i*p_i\ge S 的情况。

见代码

CODE

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 2000005;
const int mod = 1e9 + 7;		
int S, q, m, p[MAXN], cnt, a[10], s, f[20000005], inv[10];
bool vis[MAXN];
LL n;

inline int C(LL N, LL M) {
	int re = 1;
	for(int i = 1; i <= M; ++i)
		re = 1ll * re * ((N-i+1)%mod) % mod * inv[i] % mod;
	return re;
}

int main () {
	freopen("prime.in", "r", stdin);
	freopen("prime.out", "w", stdout);
	inv[0] = inv[1] = 1;
	for(int i = 2; i < 10; ++i) inv[i] = 1ll*(mod-mod/i)*inv[mod%i]%mod;
	scanf("%d%d", &S, &q); int s = S;
	for(int i = 2; i <= S; ++i) {
		if(!vis[i]) p[++cnt] = i;
		for(int j = 1; j <= cnt && p[j]*i <= S; ++j) {
			vis[p[j]*i] = 1;
			if(i % p[j] == 0) break;
		}
	}
	for(int i = 1; i <= cnt; ++i)
		if(S % p[i] == 0) a[++m] = p[i], s /= p[i];
	bool flg = 0;
	if(s > 1) flg = 1;
	else {
		s = 0;
		for(int i = 1; i <= m; ++i) s += a[i];
		f[0] = 1;
		for(int i = 1; i <= m; ++i) {
			for(int j = a[i]; j <= i*S; ++j) (f[j] += f[j-a[i]]) %= mod;
			for(int j = i*S; j >= S; --j) (f[j] -= f[j-S]) %= mod;
		}
	}
	while(q--) {
		scanf("%lld", &n);
		if(flg) puts("0");
		else {
			n -= s;
			if(n < 0) puts("0");	
			else {
				int ans = 0;
				for(int i = 0; i <= m && i*S <= n; ++i)
					ans = (ans + 1ll * f[i*S + n%S] * C((n-i*S)/S+m-1, m-1)) % mod;
				printf("%d\n", (ans + mod) % mod);
			}
		}
	}
}
发布了367 篇原创文章 · 获赞 239 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/Ike940067893/article/details/104115991