题意
小
C
C
非常擅长背包问题,他有一个奇怪的背包,这个背包有一个参数,当他向这个背包内放入若干个物品后,背包的重量是物品总体积对
P
P
取模后的结果.现在小有
n
n
种体积不同的物品,第种占用体积为
Vi
V
i
,每种物品都有无限个.他会进行
q
q
次询问,每次询问给出重量,你需要回答有多少种放入物品的方案,能将一个初始为空的背包的重量变为
wi
w
i
.注意,两种方案被认为是不同的,当且仅当放入物品的种类不同,而与每种物品放入的个数无关.不难发现总的方案数为
2n
2
n
。由于答案可能很大,你只需要输出答案对
109+7
10
9
+
7
取模的结果.
n,q≤106,P,Vi,wi≤109
n
,
q
≤
10
6
,
P
,
V
i
,
w
i
≤
10
9
分析
分析一波不难发现我们要求的就是有多少个集合满足
gcd(Vi,P)|wi
g
c
d
(
V
i
,
P
)
|
w
i
。
先把所有的
Vi
V
i
变成
gcd(Vi,P)
g
c
d
(
V
i
,
P
)
,所有的
wi
w
i
变成
gcd(wi,P)
g
c
d
(
w
i
,
P
)
,然后把所有
P
P
的约数找出来。
显然的约数个数是
O(103)
O
(
10
3
)
级别的。
那么我们对所有数进行dp,设
f[i,j]
f
[
i
,
j
]
表示前i个数中选出若干个,满足它们的
gcd
g
c
d
恰好为
j
j
的方案,然后就可以询问了。
时间复杂度
O(σ2(P))
O
(
σ
2
(
P
)
)
。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
typedef long long LL;
const int N=2005;
const int MOD=1000000007;
int n,m,p,tot,w[N],id1[100005],id2[100005],bin[1000005],s[N],f[N][N],B,ans[N];
int gcd(int x,int y)
{
if (!y) return x;
else return gcd(y,x%y);
}
void pre()
{
B=sqrt(p);
for (int i=1;i*i<=p;i++)
if (p%i==0)
{
w[++tot]=i;id1[i]=tot;
if (p/i!=i) w[++tot]=p/i,id2[i]=tot;
}
}
int main()
{
scanf("%d%d%d",&n,&m,&p);
pre();
for (int i=1;i<=n;i++)
{
int x;scanf("%d",&x);
x=gcd(x,p);
if (x<=B) s[id1[x]]++;
else s[id2[p/x]]++;
}
bin[0]=1;
for (int i=1;i<=n;i++) bin[i]=bin[i-1]*2%MOD;
f[0][id2[1]]=1;
for (int i=0;i<tot;i++)
for (int j=1;j<=tot;j++)
{
if (!f[i][j]) continue;
(f[i+1][j]+=f[i][j])%=MOD;
int x=gcd(w[i+1],w[j]),k=x<=B?id1[x]:id2[p/x];
(f[i+1][k]+=(LL)f[i][j]*(bin[s[i+1]]-1)%MOD)%=MOD;
}
for (int i=1;i<=tot;i++)
for (int j=1;j<=tot;j++)
if (w[i]%w[j]==0) (ans[i]+=f[tot][j])%=MOD;
while (m--)
{
int x;scanf("%d",&x);
x=gcd(x,p);
printf("%d\n",x<=B?ans[id1[x]]:ans[id2[p/x]]);
}
return 0;
}