【题目】
lydsy
有
n
n
n种体积不同的物品,第
i
i
i种占用
v
i
v_i
vi,每种物品都有无限个。现在需要放入一个背包中,使用空间是物品占用总和对
P
P
P取模。
Q
Q
Q次询问有多少种不同方式可以将占用变为
w
i
w_i
wi。方式不同当且仅当选择的物品种类不同。
n
,
Q
≤
1
0
6
,
P
≤
1
0
9
n,Q\leq 10^6,P\leq 10^9
n,Q≤106,P≤109,答案对
1
0
9
+
7
10^9+7
109+7取模。
【解题思路】
对于每个
v
i
v_i
vi,就是构造
v
i
k
≡
x
(
mod
P
)
v_ik\equiv x (\text{mod }P)
vik≡x(mod P),看是否能构造出需要的
x
x
x。我们可以将其写出
a
x
+
b
y
=
c
ax+by=c
ax+by=c的形式,根据裴蜀定理,这个等式有整数解,当且仅当
g
c
d
(
a
,
b
)
∣
c
gcd(a,b)|c
gcd(a,b)∣c。
那么对于每个选择
v
i
v_i
vi的方案,我们能构造出的所有占用,就是所有数与
P
P
P的
g
c
d
gcd
gcd的倍数。
同样对于每个询问
w
i
w_i
wi,我们也可以先将
w
i
w_i
wi与
P
P
P取
g
c
d
gcd
gcd,这并不会影响答案。
那么现在就是要求
P
P
P的每个约数的答案了,而
P
P
P的约数个数大概是小于
O
(
P
1
3
)
O(P^{\frac 1 3})
O(P31)的。
不妨考虑设
f
i
,
j
f_{i,j}
fi,j表示前
i
i
i个数,构造出答案为
j
j
j的方案数,设
S
S
S为
P
P
P的约数个数,
g
c
d
gcd
gcd是
O
(
1
)
O(1)
O(1)的,这样可以做到
O
(
n
S
)
O(nS)
O(nS)。
但实际上由于每个 v i v_i vi有用的因子一定也是 P P P的约数,所以实际上本质不同的 v i v_i vi只有 S S S个,转移的时候乘上一个 2 k − 1 2^k-1 2k−1即可。
这样上面的 DP \text{DP} DP就可以做到 O ( S 2 ) O(S^2) O(S2)了(写 map \text{map} map的话是 O ( S 2 log P ) O(S^2\log P) O(S2logP),还有预处理的 O ( P ) O(\sqrt P) O(P)
【参考代码】
#include<bits/stdc++.h>
using namespace std;
const int N=1500,M=1e6+10,mod=1e9+7;
namespace IO
{
int read()
{
int ret=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return ret;
}
void write(int x){if(x>9)write(x/10);putchar(x%10^48);}
void writeln(int x){write(x);putchar('\n');}
}
using namespace IO;
namespace DreamLolita
{
int n,Q,P,cnt;
int fc[M],p[N],a[N],ans[N],f[N][N];
map<int,int>mp;
void up(int &x,int y){x+=y;if(x>=mod)x-=mod;}
int gcd(int x,int y){return y?gcd(y,x%y):x;}
void solution()
{
n=read();Q=read();P=read();p[cnt=1]=1;mp[0]=0;
fc[0]=1;for(int i=1;i<=n;++i)fc[i]=1ll*fc[i-1]*2%mod;
for(int i=1;i<=n;++i)up(fc[i],mod-1);
for(int i=2;i<=sqrt(P);++i) if(!(P%i)) p[++cnt]=i,p[++cnt]=P/i;
if(p[cnt]==p[cnt-1]) --cnt;
sort(p+1,p+cnt+1);
for(int i=1;i<=cnt;++i) mp[p[i]]=i;
for(int i=1;i<=n;++i)
{
int x=gcd(P,read());
a[mp[x]]++;
}
f[0][0]=1;
for(int i=1;i<=cnt;++i)
{
memcpy(f[i],f[i-1],sizeof(f[i-1]));
if(!a[i]) continue;
for(int j=0;j<=cnt;++j)
{
int t=gcd(p[j],p[i]);
up(f[i][mp[t]],1ll*fc[a[i]]*f[i-1][j]%mod);
}
}
for(int i=1;i<=cnt;++i) for(int j=1;j<=i;++j)
if(!(p[i]%p[j])) up(ans[i],f[cnt][j]);
while(Q--)
{
int x=gcd(P,read());
writeln(ans[mp[x]]);
}
}
}
int main()
{
#ifdef Durant_Lee
freopen("BZOJ5302.in","r",stdin);
freopen("BZOJ5302.out","w",stdout);
#endif
DreamLolita::solution();
return 0;
}