题意
- 有一个奇怪的背包,背包的重量是放入其中的物品总体积对 P P P取模后的结果。现有 n n n种体积不同的物品,第 i i i种占用体积为 V i V_i Vi,每种物品都有无限个。 q q q次询问,每次询问给出重量 w i w_i wi,你需要回答有多少种放入物品的方案,能将一个初始为空的背包的重量变为 w i w_i wi。注意,两种方案被认为是不同的,当且仅当放入物品的种类不同。输出答案对 1 0 9 + 7 10^9 + 7 109+7取模的结果。
首先对于一个体积为 v v v的物品,能取到的方案为 k ∗ g c d ( v , P ) ( k ∈ [ 1 , P g c d ( v , P ) ] k * gcd(v, P)(k∈[1, \frac{P}{ gcd(v, P)}] k∗gcd(v,P)(k∈[1,gcd(v,P)P],那么我们可以把这个物品的体积看作 g c d ( v , P ) gcd(v, P) gcd(v,P),那么体积相同的物品是可以合并的,又因为 1 e 9 1e9 1e9以内一个数的约数个数是 O ( n 1 3 ) O(n^{\frac{1}{3}}) O(n31)级别的,所以物品数就变成了 P 1 3 P^{\frac{1}{3}} P31。
我们可以写出一个 d p dp dp方程,设 f [ i ] [ j ] f[i][j] f[i][j]为取了前 i i i个物品,当前选的物品的 g c d gcd gcd最小数是 P P P的第 j j j个约数的方案数,那么转移可以用刷表法,枚举用哪个取转移,可以转移到 g c d ( a [ i ] , a [ j ] ) gcd(a[i], a[j]) gcd(a[i],a[j])这个数上,再把之前的方案累加上去就做完了。
但是我们现在查询的话要枚举一个数的所有约数,是 O ( P 1 3 ) O(P^{\frac{1}{3}}) O(P31)级别的,对于这个我们枚举一个数倍数,用 O ( P 1 3 ln P 1 3 ) O(P^{\frac{1}{3}}\ln P^{\frac{1}{3}}) O(P31lnP31)的时间处理所有的答案,那么我们就可以 O ( log w ) O(\log w) O(logw)的时间回答询问了。
#include<bits/stdc++.h>
#include<bits/extc++.h>
#define file(s) freopen(s".in", "r", stdin), freopen(s".out", "w", stdout)
#define go(x, i) for(register int i = head[x]; i; i = nxt[i])
#define For(i, a, b) for(register int i = (a), i##_end_ = (b); i <= i##_end_; ++ i)
#define FOR(i, a, b) for(register int i = (a), i##_end_ = (b); i >= i##_end_; -- i)
#define debug(x) cout << #x << " = " << x << endl
#define mem(a, b) memset(a, b, sizeof(a))
#define cpy(a, b) memcpy(a, b, sizeof(a))
#define hsb __gnu_pbds::gp_hash_table
#define min(a, b) (a < b ? a : b)
#define max(a, b) (b < a ? a : b)
#define getc getchar_unlocked
#define inf (0x3f3f3f3f)
#define INF (1e18)
#define pb push_back
#define mp make_pair
#define x first
#define y second
#define y1 orzorz
#define div ylsakaaa
typedef unsigned long long ull;
typedef unsigned int uint;
typedef long long ll;
typedef std::pair<ll, int> PLI;
typedef std::pair<int, int> PII;
typedef long double ldb;
typedef double db;
inline int read() {
int _ = 0; static char __; int ___ = 1;
for(__ = getc(); !isdigit(__); __ = getc()) if(__ == '-') ___ = -1;
for(; isdigit(__); __ = getc()) _ = (_ << 3) + (_ << 1) + (__ ^ 48);
return _ * ___;
}
using namespace std;
const int N = 1e6 + 10;
const int M = 4e3 + 10;
const int mod = 1e9 + 7;
int f[M][M], a[N], div[N], pow2[N], s[M], g[M];
int n, q, P, cnt;
void get_div(int mx) {
For(i, 1, mx) if(P % i == 0)
div[++ cnt] = i, div[++ cnt] = P / i;
sort(div + 1, div + cnt + 1);
cnt = unique(div + 1, div + cnt + 1) - div - 1;
}
int main() {
#ifdef ylsakioi
file("4495");
#endif
n = read(), q = read(), P = read();
pow2[0] = 1; get_div(sqrt(P));
For(i, 1, n) {
++ s[lower_bound(div + 1, div + cnt + 1, a[i] = __gcd(P, read())) - div];
pow2[i] = pow2[i - 1] * 2 % mod;
}
sort(a + 1, a + n + 1); n = unique(a + 1, a + n + 1) - a - 1;
For(i, 1, n) {
int tmp = lower_bound(div + 1, div + cnt + 1, a[i]) - div;
f[i][tmp] = pow2[s[tmp]] - 1;
For(j, 1, cnt) {
int to = lower_bound(div + 1, div + cnt + 1, __gcd(a[i], div[j])) - div;
(f[i][to] += (ll)f[i - 1][j] * (pow2[s[tmp]] - 1) % mod) %= mod;
}
For(j, 1, cnt) (f[i][j] += f[i - 1][j]) %= mod;
}
For(j, 1, cnt) For(k, j, cnt)
if(div[k] % div[j] == 0)
(g[k] += f[n][j]) %= mod;
while(q --) printf("%d\n", g[lower_bound(div + 1, div + cnt + 1, __gcd(P, read())) - div]);
return 0;
}