题目大意
有一个模P意义下的背包,n个物品,每种有无限个,q个询问,问重量为w的方案。
题解:
首先,先考虑如何判断一些物品能否组成重w的背包。
根据贝祖定理,只要这些数和P的最大公约数是w的约数,就可以。
所以,对于本题,就是判断(2^n)中方案中,有多少种方案使得选择的数和P的最大公约数是w的约数。
显然,有一个(O((n+q)*约数个数))的dp,会超时。
考虑优化:
对于询问,枚举w的约数x,根据之前的分析,只有x是P的约数才有解。
所以,枚举(gcd(w,P))的约数即可。
由于P的约数不多,所以可以枚举P的约数y,预处理(gcd(w,P)=y)时的答案。
对于DP个过程,也可以用类似的方法:
由于要和P取gcd,所以每个V与(gcd(V,P))等价,而不同的(gcd(V,P))只有(O(P的约数个数))个。
这样,枚举(gcd(V,P))进行DP,就行了。
由于要用map定位约数,时间复杂度为(O(M^2logM+(n+q)logP)),能过。(M为P的约数个数)
代码
#include <stdio.h>
#include <map>
using namespace std;
#define md 1000000007
map < int,int > mp;
int gcd(int a, int b) {
while (b != 0) {
int t = a % b;
a = b;
b = t;
}
return a;
}
int ys[1500],m = 0,dp[1500][1500],sl[1500],mi[1000010],ans[1500];
int main() {
int n,q,P;
scanf("%d%d%d", &n, &q, &P);
for (int i = 1; 1ll * i * i <= P; i++) {
if (P % i == 0) {
ys[m++] = i;
if (P / i != i) ys[m++] = P / i;
}
}
for (int i = 0; i < m; i++) mp[ys[i]] = i;
for (int i = 0; i < n; i++) {
int a;
scanf("%d", &a);
sl[mp[gcd(P, a)]] += 1;
}
mi[0] = 1;
for (int i = 1; i <= n; i++) mi[i] = (mi[i - 1] + mi[i - 1]) % md;
dp[0][1] = 1;
for (int i = 0; i < m; i++) {
for (int j = 0; j < m; j++) {
dp[i + 1][j] = (dp[i + 1][j] + dp[i][j]) % md;
int t = mp[gcd(ys[j], ys[i])];
dp[i + 1][t] = (dp[i + 1][t] + 1ll * (mi[sl[i]] - 1 + md) * dp[i][j]) % md;
}
}
for (int i = 0; i < m; i++) {
for (int j = 0; j < m; j++) {
if (ys[i] % ys[j] == 0) ans[i] = (ans[i] + dp[m][j]) % md;
}
}
for (int i = 0; i < q; i++) {
int a;
scanf("%d", &a);
printf("%d
", ans[mp[gcd(a, P)]]);
}
return 0;
}