版权声明:这是蒟蒻的BLOG,神犇转载也要吱一声哦~ https://blog.csdn.net/Dream_Lolita/article/details/88409913
【题目】
lydsy
有
种体积不同的物品,第
种占用
,每种物品都有无限个。现在需要放入一个背包中,使用空间是物品占用总和对
取模。
次询问有多少种不同方式可以将占用变为
。方式不同当且仅当选择的物品种类不同。
,答案对
取模。
【解题思路】
对于每个
,就是构造
,看是否能构造出需要的
。我们可以将其写出
的形式,根据裴蜀定理,这个等式有整数解,当且仅当
。
那么对于每个选择
的方案,我们能构造出的所有占用,就是所有数与
的
的倍数。
同样对于每个询问
,我们也可以先将
与
取
,这并不会影响答案。
那么现在就是要求
的每个约数的答案了,而
的约数个数大概是小于
的。
不妨考虑设
表示前
个数,构造出答案为
的方案数,设
为
的约数个数,
是
的,这样可以做到
。
但实际上由于每个 有用的因子一定也是 的约数,所以实际上本质不同的 只有 个,转移的时候乘上一个 即可。
这样上面的 就可以做到 了(写 的话是 ,还有预处理的
【参考代码】
#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;
}